Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions docs/skills/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Skills for ADK agents

<div class="language-support-tag">
<span class="lst-supported">Supported in ADK</span><span class="lst-python">Python v1.25.0</span><span class="lst-typescript">TypeScript v0.6.1</span><span class="lst-go">Go v1.2.0</span><span class="lst-preview">Experimental</span>
<span class="lst-supported">Supported in ADK</span><span class="lst-python">Python v1.25.0</span><span class="lst-typescript">TypeScript v0.6.1</span><span class="lst-go">Go v1.2.0</span><span class="lst-kotlin">Kotlin v0.1.0</span><span class="lst-preview">Experimental</span>
</div>

An agent ***Skill*** is a self-contained unit of functionality that an ADK agent
Expand All @@ -17,7 +17,8 @@ impact on the operating context window of the agent.
respective ADK GitHub repositories:
[ADK Python](https://github.com/google/adk-python/issues/new?template=feature_request.md&labels=skills),
[ADK TypeScript](https://github.com/google/adk-js/issues/new?template=feature_request.md&labels=skills),
[ADK Go](https://github.com/google/adk-go/issues/new?template=feature_request.md&labels=skills).
[ADK Go](https://github.com/google/adk-go/issues/new?template=feature_request.md&labels=skills),
[ADK Kotlin](https://github.com/google/adk-kotlin/issues/new).

## Get started

Expand Down Expand Up @@ -101,6 +102,15 @@ You can define [skills in code](#inline-skills) or load
For a complete example, see the code sample in
[skills](https://github.com/google/adk-go/tree/main/examples/skills).

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/skills/SkillsExample.kt:get_started"
```

For a complete example, see the code sample in
[skills](https://github.com/google/adk-kotlin/tree/main/examples/src/main/kotlin/com/google/adk/kt/examples/skills).

!!! note "Check your working directory"

Ensure that 'skills/' directory exist in your current working directory and contains the sub-directories for the Skills you want to use in your agent.
Expand Down Expand Up @@ -288,6 +298,17 @@ You can define Skills within the code of your agent, as shown below.
}
```

=== "Kotlin"

!!! note
ADK Kotlin does not currently provide a standard Source for inline skills.
To define skills directly in code, you must implement the `SkillSource`
interface yourself, as shown below.

```kotlin
--8<-- "examples/kotlin/snippets/skills/SkillsExample.kt:inline_skill"
```

!!! note
The `Source` interface can be backed by any data store (such as a database)
to support dynamic use cases like live updates and personalization.
Expand Down Expand Up @@ -343,6 +364,12 @@ You can define Skills within the code of your agent, as shown below.
}
```

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/skills/SkillsExample.kt:filesystem_skill"
```

## Skill processing and validation

When you include skills in your agent, the agent uses a standardized process
Expand All @@ -356,4 +383,5 @@ Check out these resources for building agents with Skills:

- [Skills in Python - code sample](https://github.com/google/adk-python/tree/main/contributing/samples/environment_and_skills/skills_agent)
- [Skills in Go - code sample](https://github.com/google/adk-go/tree/main/examples/skills)
- [Skills in Kotlin - code sample](https://github.com/google/adk-kotlin/tree/main/examples/src/main/kotlin/com/google/adk/kt/examples/skills)
- Agent Skills [specification documentation](https://agentskills.io/)
133 changes: 133 additions & 0 deletions examples/kotlin/snippets/skills/SkillsExample.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.adk.kt.examples.skills

import com.google.adk.kt.agents.Instruction
import com.google.adk.kt.agents.LlmAgent
import com.google.adk.kt.models.Gemini
import com.google.adk.kt.skills.Frontmatter
import com.google.adk.kt.skills.NewFileSystemSource
import com.google.adk.kt.skills.SkillSource
import com.google.adk.kt.skills.SkillSourceException
import com.google.adk.kt.tools.SkillToolset

// --8<-- [start:get_started]
// NewFileSystemSource discovers every skill directory under the base directory,
// so there is no per-skill load call.
val mySkillToolset = SkillToolset(NewFileSystemSource("skills"))

val skillUserAgent =
LlmAgent(
name = "skill_user_agent",
model = Gemini(name = "gemini-flash-latest"),
description = "An agent that can use specialized skills.",
instruction =
Instruction("You are a helpful assistant that can leverage skills to perform tasks."),
// A SkillToolset contributes only the skill tools. Any other tool the agent
// needs is passed separately in `tools`.
toolsets = listOf(mySkillToolset),
)
// --8<-- [end:get_started]

// --8<-- [start:inline_skill]

/**
* ADK Kotlin does not provide a standard [SkillSource] for skills defined in code, so implement the
* interface yourself to serve them from memory.
*/
class StaticSkillSource : SkillSource {
private val greetingSkill =
Frontmatter(
name = "greeting-skill",
description = "A friendly greeting skill that can say hello to a specific person.",
)

private val instructions =
"Step 1: Read the 'references/hello_world.txt' file to understand how to greet the " +
"user. Step 2: Return a greeting based on the reference."

private val resources =
mapOf(
"references/hello_world.txt" to "Hello! So glad to have you here!",
"references/example.md" to "This is an example reference.",
)

private fun notFound(skillName: String) = SkillSourceException("Skill $skillName not found.")

override suspend fun listFrontmatters(): Result<List<Frontmatter>> =
Result.success(listOf(greetingSkill))

override suspend fun loadFrontmatter(skillName: String): Result<Frontmatter> =
if (skillName == greetingSkill.name) {
Result.success(greetingSkill)
} else {
Result.failure(notFound(skillName))
}

override suspend fun loadInstructions(skillName: String): Result<String> =
if (skillName == greetingSkill.name) {
Result.success(instructions)
} else {
Result.failure(notFound(skillName))
}

override suspend fun listResources(
skillName: String,
resourceDirectoryPath: String,
): Result<List<String>> {
if (skillName != greetingSkill.name) return Result.failure(notFound(skillName))
val prefix = resourceDirectoryPath.removePrefix("./").removeSuffix("/")
if (prefix.isEmpty() || prefix == ".") return Result.success(resources.keys.toList())
// Skill resources live only under references/, assets/ and scripts/.
if (prefix.substringBefore("/") !in SkillSource.VALID_RESOURCE_DIRS) {
return Result.failure(
SkillSourceException("Invalid resource path: $resourceDirectoryPath"),
)
}
return Result.success(resources.keys.filter { it.startsWith("$prefix/") })
}

override suspend fun loadResource(
skillName: String,
resourcePath: String,
): Result<ByteArray> {
if (skillName != greetingSkill.name) return Result.failure(notFound(skillName))
val content =
resources[resourcePath]
?: return Result.failure(
SkillSourceException("Resource $resourcePath not found in skill $skillName."),
)
return Result.success(content.encodeToByteArray())
}
}

val inlineSkillAgent =
LlmAgent(
name = "greeting_agent",
model = Gemini(name = "gemini-flash-latest"),
instruction = Instruction("Greet the user by following the greeting skill."),
toolsets = listOf(SkillToolset(StaticSkillSource())),
)
// --8<-- [end:inline_skill]

// --8<-- [start:filesystem_skill]
// Every immediate subdirectory of "skills" that contains a SKILL.md is exposed as
// a skill, so individual skills are discovered rather than named one by one.
val filesystemSource = NewFileSystemSource("skills")

val filesystemSkillToolset = SkillToolset(filesystemSource)
// --8<-- [end:filesystem_skill]
1 change: 1 addition & 0 deletions tools/kotlin-snippets/files_to_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ snippets/tools/overview/UserPreferenceTools.kt
snippets/tools/overview/CustomerSupport.kt
snippets/tools/overview/DocAnalysisTools.kt
snippets/tools/overview/OrderTools.kt
snippets/skills/SkillsExample.kt
Loading