Skill
Skill Middleware provides Skill support for Eino ADK Agents, enabling agents to dynamically discover and use predefined skills to complete tasks.
What is a Skill
A Skill is a folder containing instructions, scripts, and resources. Agents can discover and use these Skills on demand to extend their capabilities. The core is a SKILL.md file, which includes metadata (at least name and description) and instructions guiding the Agent to execute tasks.
my-skill/
├── SKILL.md # Required: instructions + metadata
├── scripts/ # Optional: executable code
├── references/ # Optional: reference docs
└── assets/ # Optional: templates/resources
Skills use Progressive Disclosure to efficiently manage context:
- Discovery: The Agent only loads the name and description of each available Skill — enough to determine when the Skill might be needed
- Activation: When a task matches a Skill, the Agent loads the full
SKILL.mdcontent into context - Execution: The Agent follows instructions to execute the task, loading other files or executing bundled code as needed
💡 Ref: https://agentskills.io/home
Interfaces
FrontMatter
Skill metadata structure, parsed from the YAML frontmatter of SKILL.md. Used for quick display during the discovery phase:
type FrontMatter struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Context ContextMode `yaml:"context"`
Agent string `yaml:"agent"`
Model string `yaml:"model"`
}
| Field | Type | Description |
Name | string | Unique identifier of the Skill. Use short, meaningful names (e.g. pdf-processing, web-research) |
Description | string | Description of the Skill's capabilities. Key basis for the Agent to decide whether to use it — should clearly describe applicable scenarios and capabilities |
Context | ContextMode | Context mode. Values: fork(isolated context), fork_with_context(copy history messages). Empty means inline mode |
Agent | string | Agent name to use, used with Context, resolved via AgentHub. Empty uses the default Agent |
Model | string | Model name to use, resolved via ModelHub |
ContextMode
const (
ContextModeFork ContextMode = "fork" // Isolated context
ContextModeForkWithContext ContextMode = "fork_with_context" // Copy history messages
)
| Mode | Description |
| Inline (default) | Skill content is returned directly as the tool result, and the current Agent continues processing |
fork_with_context | Creates a new Agent, copies the current conversation history, executes the Skill task independently, and returns the result |
fork | Creates a new Agent with isolated context (only Skill content), executes independently, and returns the result |
Skill
Complete Skill structure, containing metadata and instruction content:
type Skill struct {
FrontMatter
Content string
BaseDirectory string
}
| Field | Type | Description |
FrontMatter | FrontMatter | Embedded metadata structure |
Content | string | Body content of SKILL.md after frontmatter, containing detailed instructions, workflows, examples, etc. |
BaseDirectory | string | Absolute path of the Skill directory, through which the Agent can access other resource files in the directory |
Backend
Skill backend interface, decoupling skill storage from usage:
type Backend interface {
List(ctx context.Context) ([]FrontMatter, error)
Get(ctx context.Context, name string) (Skill, error)
}
| Method | Description |
List | List metadata of all available skills. Called when the Agent starts to build the skill tool description |
Get | Get complete skill content by name. Called when the Agent decides to use a skill |
NewBackendFromFilesystem
Backend implementation based on the filesystem.Backend interface, scanning first-level subdirectories under a specified directory to read skills:
type BackendFromFilesystemConfig struct {
Backend filesystem.Backend
BaseDir string
}
func NewBackendFromFilesystem(ctx context.Context, config *BackendFromFilesystemConfig) (Backend, error)
| Field | Type | Required | Description |
Backend | filesystem.Backend | Yes | Filesystem backend implementation for file operations |
BaseDir | string | Yes | Skill root directory path. Scans first-level subdirectories under this directory, looking for directories containing SKILL.mdfiles |
How it works:
- Scans first-level subdirectories under
BaseDir - Looks for
SKILL.mdin each subdirectory - Parses YAML frontmatter to get metadata
- Deeply nested
SKILL.mdfiles are ignored
filesystem.Backend has two implementations to choose from — see the FileSystem Backend documentation.
AgentHub and ModelHub
When Skills use Context mode (fork / fork_with_context), AgentHub and ModelHub are needed to provide Agent instances and model instances.
💡 The following shows non-generic alias types (i.e.,
*schema.Messagespecialization). Generic versionsTypedAgentHub[M]andTypedModelHub[M]are available for*schema.AgenticMessagescenarios, with identical interface signatures differing only in the message type parameter.
// AgentHubOptions passed to AgentHub.Get
type AgentHubOptions = TypedAgentHubOptions[*schema.Message]
type TypedAgentHubOptions[M adk.MessageType] struct {
// Model is the model instance specified in the skill frontmatter (resolved via ModelHub).
// nil means the skill did not specify a model override; implementations should use the default model.
Model model.BaseModel[M]
}
// AgentHub provides Agent instances for Context mode
type AgentHub = TypedAgentHub[*schema.Message]
type TypedAgentHub[M adk.MessageType] interface {
// Get returns an Agent by name. When name is empty, should return the default Agent.
Get(ctx context.Context, name string, opts *TypedAgentHubOptions[M]) (adk.TypedAgent[M], error)
}
// ModelHub resolves model instances by name
type ModelHub = TypedModelHub[*schema.Message]
type TypedModelHub[M adk.MessageType] interface {
Get(ctx context.Context, name string) (model.BaseModel[M], error)
}
💡 Note: The return type of
AgentHubOptions.ModelandModelHub.Getismodel.BaseModel[M], not themodel.ToolCallingChatModelfrom older documentation.
SubAgentInput and SubAgentOutput
These two structs are used when customizing fork mode behavior:
type SubAgentInput = TypedSubAgentInput[*schema.Message]
type TypedSubAgentInput[M adk.MessageType] struct {
Skill Skill
Mode ContextMode
RawArguments string // Raw JSON arguments
SkillContent string // Constructed Skill content
History []M // Conversation history (only in fork_with_context mode)
ToolCallID string // Tool call ID (only in fork_with_context mode)
}
type SubAgentOutput = TypedSubAgentOutput[*schema.Message]
type TypedSubAgentOutput[M adk.MessageType] struct {
Skill Skill
Mode ContextMode
RawArguments string
Messages []M // All messages produced by the sub-Agent
Results []string // Extracted assistant message text content
}
Initialization
Config
type Config = TypedConfig[*schema.Message]
type TypedConfig[M adk.MessageType] struct {
Backend Backend
SkillToolName *string
AgentHub TypedAgentHub[M]
ModelHub TypedModelHub[M]
CustomSystemPrompt SystemPromptFunc
CustomToolDescription ToolDescriptionFunc
CustomToolParams func(ctx context.Context, defaults map[string]*schema.ParameterInfo) (map[string]*schema.ParameterInfo, error)
BuildContent func(ctx context.Context, skill Skill, rawArgs string) (string, error)
BuildForkMessages func(ctx context.Context, in TypedSubAgentInput[M]) ([]M, error)
FormatForkResult func(ctx context.Context, in TypedSubAgentOutput[M]) (string, error)
}
| Field | Type | Required | Default | Description |
Backend | Backend | Yes | - | Skill backend implementation, responsible for skill storage and retrieval |
SkillToolName | *string | No | "skill" | Skill tool name. Can be customized to avoid conflicts if a tool with the same name already exists |
AgentHub | TypedAgentHub[M] | No | - | Provides Agent instances. Required when using context: forkor fork_with_context |
ModelHub | TypedModelHub[M] | No | - | Provides model instances. Passed to AgentHub in Context mode; in inline mode, switches the model for subsequent ChatModel calls via WrapModel |
CustomSystemPrompt | SystemPromptFunc | No | Built-in prompt | Custom system prompt. Signature: func(ctx, toolName) string |
CustomToolDescription | ToolDescriptionFunc | No | Built-in description | Custom tool description. Signature: func(ctx, skills []FrontMatter) string |
CustomToolParams | func | No | Only skillparam | Custom tool parameter schema. Receives defaults and returns custom params; skillis always required |
BuildContent | func | No | Default formatting | Custom Skill content generation, can inject additional context into the content |
BuildForkMessages | func | No | See below | Custom initial messages for the sub-Agent in fork mode. Default: fork→ [UserMessage(content)], fork_with_context→ [history..., ToolMessage(content, callID)] |
FormatForkResult | func | No | Concatenate content | Custom sub-Agent result formatting. Default concatenates assistant message content |
NewMiddleware
func NewMiddleware(ctx context.Context, config *Config) (adk.ChatModelAgentMiddleware, error)
Creates the Skill Middleware, returns adk.ChatModelAgentMiddleware, used in ChatModelAgentConfig.Handlers.
💡 Generic version
NewTyped[M](ctx, config)returnsadk.TypedChatModelAgentMiddleware[M], usable with*schema.AgenticMessagetype Agents.
Usage Example
// 1. Create Backend
backend, err := skill.NewBackendFromFilesystem(ctx, &skill.BackendFromFilesystemConfig{
Backend: fsBackend,
BaseDir: "/path/to/skills",
})
if err != nil {
return err
}
// 2. Create Middleware
handler, err := skill.NewMiddleware(ctx, &skill.Config{
Backend: backend,
AgentHub: myAgentHub, // Optional, only needed for fork mode
ModelHub: myModelHub, // Optional, only needed when using model field
})
if err != nil {
return err
}
// 3. Pass to Agent's Handlers
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
// ... other config
Handlers: []adk.ChatModelAgentMiddleware{handler},
})
