Skills for Agents:Agent 技能扩展

如果说 Tools 是 Agent 的"手脚"——让它能够执行具体的操作(查询数据库、调用 API、发送邮件),那么 Skills 就是 Agent 的"专业知识库"——让它在特定领域内具备系统化的思维框架、最佳实践和领域语言。Skill 机制是 ADK Go 中实现 Agent 专业化、模块化和可复用性的核心架构组件。

从 Tool 到 Skill:能力抽象的升级

理解 Skill 与 Tool 的区别对于正确使用 ADK Go 至关重要:

维度ToolSkill
抽象层级原子操作(单个函数调用)能力模块(包含思维链、多步骤流程)
状态管理通常无状态可维护内部状态和上下文
复杂度简单、单一职责复杂、领域专用
复用方式作为函数被调用作为模块被挂载
示例query_databasesend_email数据分析代码审查合同审核

一个 Skill 本质上是一个预编排的 Agent 行为模板。它可能包含:

  • 领域特定的系统指令(System Instruction)
  • 推荐的 Tool 组合
  • 预设的思维链(Chain-of-Thought)模板
  • 输出格式规范
  • 错误处理和回退策略

例如,“数据分析 Skill"不仅包含调用 Python 解释器的 Tool,还包含"如何理解数据分布→选择合适图表→生成洞察"的完整思维框架。

ADK Go 内置 Skills 深度解析

ADK Go 提供了若干开箱即用的内置 Skills,每个都针对特定场景进行了深度优化。

Code Interpreter Skill

这是最常用的内置 Skill 之一,它赋予 Agent 执行代码的能力。但不同于简单地暴露一个 exec_python Tool,Code Interpreter Skill 封装了完整的代码执行沙箱、错误处理和结果格式化逻辑。

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "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.Getenv("GOOGLE_API_KEY"),
        Model:  "gemini-2.0-pro",
    })
    if err != nil {
        log.Fatalf("模型初始化失败: %v", err)
    }

    // 配置 Code Interpreter Skill
    codeSkill := skill.CodeInterpreter(
        skill.WithTimeout(30*time.Second),        // 代码执行超时
        skill.WithMaxMemory(512*1024*1024),       // 内存限制 512MB
        skill.WithAllowedImports([]string{        // 允许导入的包白名单
            "numpy", "pandas", "matplotlib", "json", "math",
        }),
        skill.WithForbiddenPatterns([]string{     // 禁止的危险操作
            `os\.system`, `subprocess\.call`, `eval\(`, `exec\(`,
        }),
    )

    agent, err := agent.New(agent.Config{
        Name:  "data-analyst",
        Model: model,
        Instruction: `你是一个数据分析师。当用户上传数据或询问数据相关问题时:
1. 先理解数据的结构和类型
2. 使用 Code Interpreter 进行探索性数据分析(EDA)
3. 生成可视化图表辅助说明
4. 提供数据驱动的洞察和建议
5. 所有分析必须基于实际代码执行结果,不得编造数据`,
        Skills: []skill.Skill{codeSkill},
    })
    if err != nil {
        log.Fatalf("Agent 创建失败: %v", err)
    }

    resp, err := agent.Run(ctx, "分析这份销售数据的趋势:\n月份,销售额\n1月,15000\n2月,18000\n3月,22000\n4月,19000\n5月,25000")
    if err != nil {
        log.Fatalf("执行失败: %v", err)
    }

    fmt.Println(resp.Text)
}

生产环境注意事项

  1. 沙箱隔离:Code Interpreter 必须在完全隔离的环境中运行。我们推荐使用 gVisor、Firejail 或容器技术实现额外的安全层。

  2. 资源限制:未加限制的代码执行可能导致 CPU 耗尽或内存溢出。始终设置 MaxMemoryTimeout

  3. 网络隔离:默认情况下,代码执行环境应禁止网络访问,防止数据外泄或恶意下载。

  4. 状态持久化:如果需要在多次调用间保持变量状态,使用 Skill 提供的 Session 存储机制,而非全局变量。

Web Search Skill

Web Search Skill 封装了搜索引擎调用、结果筛选、内容提取和可信度评估的完整流程。它与 Grounding 的区别在于:Grounding 是隐式的、自动的搜索增强,而 Web Search Skill 是显式的、由 Agent 主动调用的研究能力。

webSearchSkill := skill.WebSearch(
    skill.WithSearchProvider(skill.GoogleSearchProvider{
        APIKey: os.Getenv("GOOGLE_SEARCH_API_KEY"),
        CX:     os.Getenv("GOOGLE_SEARCH_CX"),
    }),
    skill.WithResultLimit(10),
    skill.WithContentExtractor(skill.ReadabilityExtractor{}),  // 使用 Readability 算法提取正文
    skill.WithCredibilityScorer(skill.DomainCredibilityScorer{
        // 可信域名加分
        TrustedDomains: []string{"arxiv.org", "github.com", "wikipedia.org"},
        // 低质量域名降分
        BlockedDomains: []string{"spam-site.com"},
    }),
)

Data Analysis Skill

Data Analysis Skill 是 Code Interpreter Skill 的上层封装,专门针对结构化数据分析场景优化。它内置了:

  • 数据清洗和预处理模板
  • 常用统计检验方法
  • 自动图表类型推荐(基于数据特征选择最合适的可视化方式)
  • 异常值检测和处理策略
dataSkill := skill.DataAnalysis(
    skill.WithVisualizationBackend(skill.MatplotlibBackend{
        OutputFormat: "png",
        DPI:          150,
    }),
    skill.WithStatisticalTests([]string{"t-test", "chi-square", "anova"}),
)

自定义 Skill:构建领域专家 Agent

内置 Skills 覆盖了通用场景,但企业级应用往往需要针对特定业务领域构建自定义 Skill。以下是一个为"智能合同审核"场景设计的自定义 Skill 完整实现:

package contract

import (
    "context"
    "fmt"
    "regexp"
    "strings"

    "google.golang.org/adk/skill"
    "google.golang.org/adk/tool"
)

// ContractReviewSkill 封装了合同审核的专业能力
type ContractReviewSkill struct {
    riskRules       []RiskRule
    templateLibrary TemplateLibrary
    complianceDB    ComplianceDatabase
}

// RiskRule 定义一条风险识别规则
type RiskRule struct {
    Name        string
    Severity    string  // critical, high, medium, low
    Pattern     *regexp.Regexp
    Description string
    Suggestion  string
}

// NewContractReviewSkill 创建合同审核 Skill
func NewContractReviewSkill(db ComplianceDatabase) *ContractReviewSkill {
    return &ContractReviewSkill{
        riskRules: []RiskRule{
            {
                Name:        "无限责任条款",
                Severity:    "critical",
                Pattern:     regexp.MustCompile(`(?i)无限责任|unlimited liability`),
                Description: "合同包含无限责任条款,可能导致不可控的法律风险",
                Suggestion:  "建议修改为'有限责任',并明确赔偿上限",
            },
            {
                Name:        "自动续约条款",
                Severity:    "high",
                Pattern:     regexp.MustCompile(`(?i)自动续约|automatic.*renewal|tacit.*renewal`),
                Description: "存在自动续约机制,可能导致合同在不知情的情况下延续",
                Suggestion:  "建议增加明确的续约通知期(如提前 30 天书面通知)",
            },
            {
                Name:        "单方变更权",
                Severity:    "high",
                Pattern:     regexp.MustCompile(`(?i)单方变更|单方修改|unilateral.*modification`),
                Description: "一方拥有单方面修改合同条款的权利",
                Suggestion:  "建议改为'双方协商一致后方可变更'",
            },
        },
        complianceDB: db,
    }
}

func (s *ContractReviewSkill) Name() string {
    return "contract-review"
}

func (s *ContractReviewSkill) Description() string {
    return "专业的合同风险审核能力,能够识别合同中的法律风险、条款漏洞和合规问题"
}

func (s *ContractReviewSkill) Instructions() string {
    return `你是一个专业的合同审核律师助手。当用户提交合同文本时,你需要:

1. **结构分析**:识别合同的类型(采购、服务、劳动、租赁等)和主要条款
2. **风险扫描**:使用内置规则库扫描高风险条款
3. **合规检查**:对照最新法律法规检查合规性
4. **条款建议**:对模糊、不公平或缺失的条款提出修改建议
5. **生成报告**:输出结构化的审核报告,包含风险等级、问题描述和修改建议

审核原则:
- 优先保护委托方利益
- 关注可执行性(条款是否能在实际纠纷中被法院支持)
- 注意时效性(法律有效期、通知期限等)
- 保持客观中立,不夸大风险也不遗漏问题`
}

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) {
    // 预处理:标准化合同文本
    normalized := strings.ReplaceAll(input, "\r\n", "\n")
    normalized = regexp.MustCompile(`\n{3,}`).ReplaceAllString(normalized, "\n\n")
    return normalized, nil
}

func (s *ContractReviewSkill) AfterRun(ctx context.Context, output string) (string, error) {
    // 后处理:确保报告格式统一
    if !strings.Contains(output, "## 审核结论") {
        output += "\n\n## 审核结论\n\n本合同经审核发现上述风险点,建议委托方在签署前与法务部门确认。"
    }
    return output, nil
}

// 注册 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 需要 compliance_db 配置")
        }
        return NewContractReviewSkill(db), nil
    })
}

使用自定义 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 的组合与编排

复杂的业务场景往往需要多个 Skill 协同工作。ADK Go 支持 Skill 的组合模式,允许构建"Skill 管道"或"Skill 路由器”。

Skill 管道模式

数据依次流经多个 Skill,每个 Skill 对数据进行特定处理:

pipeline := skill.NewPipeline(
    skill.DataExtractionSkill{},      // 第一步:从非结构化文本提取数据
    skill.DataValidationSkill{},      // 第二步:验证数据完整性和准确性
    skill.DataAnalysisSkill{},        // 第三步:分析数据模式和趋势
    skill.ReportGenerationSkill{},    // 第四步:生成分析报告
)

agent, err := agent.New(agent.Config{
    Name:   "data-pipeline-agent",
    Skills: []skill.Skill{pipeline},
})

Skill 路由模式

根据输入类型动态选择最合适的 Skill:

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},
})

生产环境中的 Skill 管理

Skill 版本控制

Skills 作为 Agent 的核心能力组件,其变更需要严格的版本管理:

type VersionedSkill struct {
    skill.Skill
    Version     string
    Changelog   string
    Deprecated  bool
    ReplacedBy  string  // 如果已弃用,指向替代版本
}

func (v *VersionedSkill) ValidateCompatibility(other skill.Skill) error {
    // 检查 Skill 版本兼容性
    // 例如:v2 的 Skill 是否兼容 v1 的 Agent 配置
}

Skill 热加载

生产环境中不希望因为更新 Skill 而重启整个 Agent 服务。可以实现 Skill 的热加载机制:

type HotReloadableSkill struct {
    current atomic.Value  // 存储 *skill.Skill
    watcher *fsnotify.Watcher
}

func (h *HotReloadableSkill) Load(path string) error {
    // 监听 Skill 定义文件的变化
    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 热加载失败: %v", err)
                    continue
                }
                h.current.Store(newSkill)
                log.Printf("Skill 已热更新: %s", newSkill.Name())
            }
        }
    }()
    return nil
}

func (h *HotReloadableSkill) Current() skill.Skill {
    return h.current.Load().(skill.Skill)
}

Skill 性能监控

每个 Skill 的调用延迟、成功率和资源消耗都应该被监控:

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
}

常见陷阱与最佳实践

陷阱 1:Skill 过度耦合

将太多逻辑塞进一个 Skill 会导致维护困难和复用性降低。遵循"单一职责原则",一个 Skill 只负责一个明确的专业领域。

陷阱 2:Skill 与 Tool 边界模糊

如果一个"Skill"只是简单封装了一个 Tool 调用,没有额外的领域逻辑或思维框架,那它应该是一个 Tool,而不是 Skill。

陷阱 3:忽略 Skill 的上下文窗口消耗

复杂的 Skill Instructions 会占用大量上下文窗口。监控每个 Skill 的指令长度,必要时将长指令拆分为核心指令 + 可加载的参考文档。

陷阱 4:Skill 间的状态冲突

当多个 Skill 同时被激活时,它们可能产生冲突的指令或争夺 Tool 调用权。使用明确的优先级机制和冲突解决策略。

下一步

Skills 赋予 Agent 专业化的领域能力,使其从通用助手升级为行业专家。接下来我们将探讨 Callbacks 与 Plugins——ADK Go 最强大的扩展机制,让你能够在 Agent 生命周期的每一个关键节点插入自定义逻辑,实现真正的深度定制。

Artifacts | Callbacks 与 Plugins →


想跟着学更多 Go ADK 实战?关注「全栈之巅-梦兽编程」公众号,每周更新 Go / AI 编程实战干货。