diff --git a/docs/skills/index.md b/docs/skills/index.md index 9143a7dad4..1e9d3dcc84 100644 --- a/docs/skills/index.md +++ b/docs/skills/index.md @@ -1,7 +1,7 @@ # Skills for ADK agents
- Supported in ADKPython v1.25.0TypeScript v0.6.1Go v1.2.0Experimental + Supported in ADKPython v1.25.0TypeScript v0.6.1Go v1.2.0Kotlin v0.1.0Experimental
An agent ***Skill*** is a self-contained unit of functionality that an ADK agent @@ -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 @@ -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. @@ -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. @@ -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 @@ -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/) diff --git a/examples/kotlin/snippets/skills/SkillsExample.kt b/examples/kotlin/snippets/skills/SkillsExample.kt new file mode 100644 index 0000000000..5d4e0d9bd9 --- /dev/null +++ b/examples/kotlin/snippets/skills/SkillsExample.kt @@ -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> = + Result.success(listOf(greetingSkill)) + + override suspend fun loadFrontmatter(skillName: String): Result = + if (skillName == greetingSkill.name) { + Result.success(greetingSkill) + } else { + Result.failure(notFound(skillName)) + } + + override suspend fun loadInstructions(skillName: String): Result = + if (skillName == greetingSkill.name) { + Result.success(instructions) + } else { + Result.failure(notFound(skillName)) + } + + override suspend fun listResources( + skillName: String, + resourceDirectoryPath: String, + ): Result> { + 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 { + 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] diff --git a/tools/kotlin-snippets/files_to_test.txt b/tools/kotlin-snippets/files_to_test.txt index b1902c362c..0c16697d27 100644 --- a/tools/kotlin-snippets/files_to_test.txt +++ b/tools/kotlin-snippets/files_to_test.txt @@ -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