If Tools are the Agent’s “hands and feet”—letting it perform concrete actions such as querying a database, calling an API, or sending an email—then Skills are the Agent’s “professional knowledge base”—giving it systematic thinking frameworks, best practices, and domain language in a specific field. The Skill mechanism is the core architectural component in ADK Go for making Agents specialized, modular, and reusable.
From Tool to Skill: Upgrading Capability Abstraction
Understanding the difference between Skills and Tools is crucial for using ADK Go correctly:
| Dimension | Tool | Skill |
|---|---|---|
| Abstraction level | Atomic operation (single function call) | Capability module (contains reasoning chain, multi-step workflow) |
| State management | Usually stateless | Can maintain internal state and context |
| Complexity | Simple, single responsibility | Complex, domain-specific |
| Reuse style | Called as a function | Mounted as a module |
| Examples | query_database, send_email | data_analysis, code_review, contract_review |
A Skill is essentially a pre-orchestrated Agent behavior template. It may include:
- Domain-specific system instructions
- Recommended Tool combinations
- Preset chain-of-thought templates
- Output-format specifications
- Error handling and fallback strategies
For example, the “Data Analysis Skill” not only includes the Tool for calling a Python interpreter, but also the complete thinking framework of “understand data distribution → choose appropriate chart → generate insights.”
In-Depth Look at Built-in ADK Go Skills
ADK Go provides several built-in Skills, each deeply optimized for a specific scenario.
Code Interpreter Skill
This is one of the most commonly used built-in Skills. It gives the Agent the ability to execute code. Unlike simply exposing an exec_python Tool, the Code Interpreter Skill encapsulates a complete code-execution sandbox, error handling, and result-formatting logic.
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"google.golang.org/adk/agent"
"google.golang.org/adk/llm"
"google.golang.org/adk/skill"
)
func main() {
ctx := context.Background()
model, err := llm.NewGeminiModel(ctx, llm.GeminiConfig{
APIKey: os.Get...Y"),
Model: "gemini-2.0-pro",
})
if err != nil {
log.Fatalf("Failed to initialize model: %v", err)
}
// Configure the Code Interpreter Skill
codeSkill := skill.CodeInterpreter(
skill.WithTimeout(30*time.Second), // code execution timeout
skill.WithMaxMemory(512*1024*1024), // memory limit 512 MB
skill.WithAllowedImports([]string{ // allowed package whitelist
"numpy", "pandas", "matplotlib", "json", "math",
}),
skill.WithForbiddenPatterns([]string{ // forbidden dangerous operations
`os\.system`, `subprocess\.call`, `eval\(`, `exec\(`,
}),
)
agent, err := agent.New(agent.Config{
Name: "data-analyst",
Model: model,
Instruction: `You are a data analyst. When a user uploads data or asks data-related questions:
1. First understand the data structure and types
2. Use the Code Interpreter for exploratory data analysis (EDA)
3. Generate visualizations to aid explanation
4. Provide data-driven insights and recommendations
5. All analysis must be based on actual code execution results; never fabricate data`,
Skills: []skill.Skill{codeSkill},
})
if err != nil {
log.Fatalf("Failed to create Agent: %v", err)
}
resp, err := agent.Run(ctx, "Analyze the trend of this sales data:
Month,Sales
Jan,15000
Feb,18000
Mar,22000
Apr,19000
May,25000")
if err != nil {
log.Fatalf("Execution failed: %v", err)
}
fmt.Println(resp.Text)
}
Production Notes:
Sandbox isolation: The Code Interpreter must run in a fully isolated environment. We recommend adding extra security layers with gVisor, Firejail, or container technology.
Resource limits: Unrestricted code execution can exhaust CPU or memory. Always set
MaxMemoryandTimeout.Network isolation: By default, the code-execution environment should block network access to prevent data exfiltration or malicious downloads.
State persistence: If you need to preserve variable state across multiple calls, use the Session storage mechanism provided by the Skill rather than global variables.
Web Search Skill
The Web Search Skill encapsulates the complete workflow of search-engine invocation, result filtering, content extraction, and credibility scoring. Its difference from Grounding is that Grounding is implicit, automatic search augmentation, while the Web Search Skill is an explicit, actively invoked research capability of the Agent.
webSearchSkill := skill.WebSearch(
skill.WithSearchProvider(skill.GoogleSearchProvider{
APIKey: os.Get...Y"),
CX: os.Getenv("GOOGLE_SEARCH_CX"),
}),
skill.WithResultLimit(10),
skill.WithContentExtractor(skill.ReadabilityExtractor{}), // extract body with Readability algorithm
skill.WithCredibilityScorer(skill.DomainCredibilityScorer{
// Bonus points for trusted domains
TrustedDomains: []string{"arxiv.org", "github.com", "wikipedia.org"},
// Penalty points for low-quality domains
BlockedDomains: []string{"spam-site.com"},
}),
)
Data Analysis Skill
The Data Analysis Skill is a higher-level wrapper over the Code Interpreter Skill, specifically optimized for structured-data analysis scenarios. It includes:
- Data cleaning and preprocessing templates
- Common statistical-test methods
- Automatic chart-type recommendation (choosing the most suitable visualization based on data characteristics)
- Outlier detection and handling strategies
dataSkill := skill.DataAnalysis(
skill.WithVisualizationBackend(skill.MatplotlibBackend{
OutputFormat: "png",
DPI: 150,
}),
skill.WithStatisticalTests([]string{"t-test", "chi-square", "anova"}),
)
Custom Skills: Building Domain-Expert Agents
Built-in Skills cover general scenarios, but enterprise applications often require custom Skills for specific business domains. Below is a complete implementation of a custom Skill for the “intelligent contract review” scenario:
package contract
import (
"context"
"fmt"
"regexp"
"strings"
"google.golang.org/adk/skill"
"google.golang.org/adk/tool"
)
// ContractReviewSkill encapsulates professional contract-review capabilities
type ContractReviewSkill struct {
riskRules []RiskRule
templateLibrary TemplateLibrary
complianceDB ComplianceDatabase
}
// RiskRule defines a risk-identification rule
type RiskRule struct {
Name string
Severity string // critical, high, medium, low
Pattern *regexp.Regexp
Description string
Suggestion string
}
// NewContractReviewSkill creates a contract-review Skill
func NewContractReviewSkill(db ComplianceDatabase) *ContractReviewSkill {
return &ContractReviewSkill{
riskRules: []RiskRule{
{
Name: "Unlimited Liability Clause",
Severity: "critical",
Pattern: regexp.MustCompile(`(?i)unlimited liability`),
Description: "The contract contains an unlimited liability clause, which may cause uncontrollable legal risk",
Suggestion: "Change to 'limited liability' and clarify a cap on compensation",
},
{
Name: "Automatic Renewal Clause",
Severity: "high",
Pattern: regexp.MustCompile(`(?i)automatic.*renewal|tacit.*renewal`),
Description: "An automatic-renewal mechanism exists; the contract may renew without awareness",
Suggestion: "Add an explicit renewal notice period (e.g., 30 days written notice)",
},
{
Name: "Unilateral Modification Right",
Severity: "high",
Pattern: regexp.MustCompile(`(?i)unilateral.*modification`),
Description: "One party has the right to modify contract terms unilaterally",
Suggestion: "Change to 'modification only after mutual agreement'",
},
},
complianceDB: db,
}
}
func (s *ContractReviewSkill) Name() string {
return "contract-review"
}
func (s *ContractReviewSkill) Description() string {
return "Professional contract risk-review capability that identifies legal risks, clause loopholes, and compliance issues"
}
func (s *ContractReviewSkill) Instructions() string {
return `You are a professional contract-review legal assistant. When a user submits a contract text, you must:
1. **Structural analysis**: Identify the contract type (procurement, service, labor, lease, etc.) and main clauses
2. **Risk scanning**: Use the built-in rule library to scan high-risk clauses
3. **Compliance check**: Check compliance against the latest laws and regulations
4. **Clause recommendations**: Propose modifications for vague, unfair, or missing clauses
5. **Generate report**: Output a structured review report containing risk level, issue description, and modification suggestions
Review principles:
- Prioritize the client's interests
- Focus on enforceability (whether the clause will be supported by a court in a real dispute)
- Pay attention to timeliness (validity period, notice period, etc.)
- Remain objective and neutral, neither exaggerating risks nor overlooking issues`
}
func (s *ContractReviewSkill) Tools() []tool.Tool {
return []tool.Tool{
&ClauseExtractorTool{},
&ComplianceCheckTool{DB: s.complianceDB},
&RiskCalculatorTool{},
}
}
func (s *ContractReviewSkill) BeforeRun(ctx context.Context, input string) (string, error) {
// Preprocessing: normalize contract text
normalized := strings.ReplaceAll(input, "
", "
")
normalized = regexp.MustCompile(`\n{3,}`).ReplaceAllString(normalized, "
")
return normalized, nil
}
func (s *ContractReviewSkill) AfterRun(ctx context.Context, output string) (string, error) {
// Post-processing: ensure uniform report format
if !strings.Contains(output, "## Review Conclusion") {
output += "
## Review Conclusion
The above risks were identified in this contract. The client is advised to confirm with the legal department before signing."
}
return output, nil
}
// Register the Skill
func init() {
skill.Register("contract-review", func(config map[string]interface{}) (skill.Skill, error) {
db, ok := config["compliance_db"].(ComplianceDatabase)
if !ok {
return nil, fmt.Errorf("contract-review skill requires compliance_db configuration")
}
return NewContractReviewSkill(db), nil
})
}
Using a Custom Skill
import "your-module/contract"
func main() {
db := initComplianceDB()
contractSkill := contract.NewContractReviewSkill(db)
agent, err := agent.New(agent.Config{
Name: "legal-assistant",
Model: model,
Skills: []skill.Skill{contractSkill},
})
// ...
}
Skill Composition and Orchestration
Complex business scenarios often require multiple Skills to work together. ADK Go supports Skill composition patterns, allowing you to build “Skill pipelines” or “Skill routers.”
Skill Pipeline Pattern
Data flows sequentially through multiple Skills, each performing specific processing:
pipeline := skill.NewPipeline(
skill.DataExtractionSkill{}, // step 1: extract data from unstructured text
skill.DataValidationSkill{}, // step 2: validate data integrity and accuracy
skill.DataAnalysisSkill{}, // step 3: analyze patterns and trends
skill.ReportGenerationSkill{}, // step 4: generate analysis report
)
agent, err := agent.New(agent.Config{
Name: "data-pipeline-agent",
Skills: []skill.Skill{pipeline},
})
Skill Router Pattern
Dynamically select the most appropriate Skill based on input type:
router := skill.NewRouter()
router.Register("code", skill.CodeInterpreter())
router.Register("data", skill.DataAnalysis())
router.Register("image", skill.ImageAnalysis())
router.SetDefault(skill.GeneralConversation())
agent, err := agent.New(agent.Config{
Name: "multi-skill-agent",
Skills: []skill.Skill{router},
})
Production Skill Management
Skill Version Control
As a core capability component of the Agent, Skill changes require strict version management:
type VersionedSkill struct {
skill.Skill
Version string
Changelog string
Deprecated bool
ReplacedBy string // if deprecated, points to the replacement version
}
func (v *VersionedSkill) ValidateCompatibility(other skill.Skill) error {
// Check Skill version compatibility
// For example, does v2 Skill remain compatible with v1 Agent configuration?
}
Skill Hot Reloading
In production, you do not want to restart the entire Agent service to update a Skill. You can implement a hot-reload mechanism:
type HotReloadableSkill struct {
current atomic.Value // stores *skill.Skill
watcher *fsnotify.Watcher
}
func (h *HotReloadableSkill) Load(path string) error {
// Watch the Skill definition file for changes
h.watcher.Add(path)
go func() {
for event := range h.watcher.Events {
if event.Op&fsnotify.Write == fsnotify.Write {
newSkill, err := loadSkillFromFile(path)
if err != nil {
log.Printf("Skill hot reload failed: %v", err)
continue
}
h.current.Store(newSkill)
log.Printf("Skill hot updated: %s", newSkill.Name())
}
}
}()
return nil
}
func (h *HotReloadableSkill) Current() skill.Skill {
return h.current.Load().(skill.Skill)
}
Skill Performance Monitoring
Every Skill’s call latency, success rate, and resource consumption should be monitored:
func (s *MonitoredSkill) Execute(ctx context.Context, input string) (string, error) {
start := time.Now()
result, err := s.inner.Execute(ctx, input)
duration := time.Since(start)
metrics.RecordHistogram("skill_latency", float64(duration.Milliseconds()),
metrics.Tag{"skill", s.Name()})
if err != nil {
metrics.IncrementCounter("skill_errors",
metrics.Tag{"skill", s.Name()})
}
return result, err
}
Common Pitfalls and Best Practices
Pitfall 1: Skill Over-Coupling
Stuffing too much logic into one Skill makes maintenance difficult and reuse low. Follow the single-responsibility principle: one Skill should only be responsible for one clear domain.
Pitfall 2: Blurred Boundary Between Skill and Tool
If a “Skill” simply wraps a single Tool call without extra domain logic or thinking framework, it should be a Tool, not a Skill.
Pitfall 3: Ignoring Skill Context-Window Consumption
Complex Skill instructions consume a large context window. Monitor the instruction length of each Skill, and split long instructions into core instructions plus loadable reference documents when necessary.
Pitfall 4: State Conflicts Between Skills
When multiple Skills are active at the same time, they may produce conflicting instructions or compete for Tool calls. Use clear priority mechanisms and conflict-resolution strategies.
Next Steps
Skills give Agents specialized domain capabilities, upgrading them from general assistants to industry experts. Next we will explore Callbacks and Plugins—the most powerful extension mechanism in ADK Go, letting you insert custom logic at every key point in the Agent lifecycle for true deep customization.
← Artifacts | Callbacks & Plugins →
Want to learn more Go ADK hands-on? Follow the “Full-Stack Peak — Mengshou Programming” WeChat account for weekly Go / AI programming practice updates.
