目录
一、Skill 讲解
1.1 什么是 Skill?
在 Agno 框架中,Skill(技能) 是一种为 AI Agent 提供结构化领域专业知识的机制。通过指令(Instructions)、脚本(Scripts)和参考文档(References),Skill 将特定领域的知识和能力打包成可重用的模块。
Agno Skills 基于 Anthropic 的 Agent Skills 规范,是一种标准化的技能定义方式,使 Agent 能够:
- 逐步发现技能:在系统提示中看到技能摘要
- 按需加载指令:当任务匹配时加载完整的技能指导
- 访问参考文档:根据需要查阅详细的技术文档
- 执行脚本:运行技能中包含的可执行代码
Skill 的核心组成:
my-skill/
├── SKILL.md # 必需:带有 YAML frontmatter 的指令文件
├── scripts/ # 可选:可执行脚本
│ └── helper.py
└── references/ # 可选:参考文档
└── guide.md
1.2 Skill 的作用和重要性
为什么 Skill 如此重要?
1. 按需提供领域专业知识(Domain Expertise on Demand)
传统方式需要在系统消息中填充所有可能的指令,这会导致:
- 上下文窗口过载
- Token 消耗过高
- Agent 性能下降
使用 Skill 后,可以:
- 将领域知识组织成专注的包
- Agent 只加载所需内容
- 节省 tokens 并最终降低成本
对比示例:
# ❌ 传统方式:在系统消息中硬编码所有指令
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
instructions=[
"When reviewing code, check for: 1) Style violations...",
"When analyzing data, follow these steps...",
"When writing documentation, use these templates...",
# ... 数百行指令
]
)
# ✅ 使用 Skill:按需加载
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
skills=Skills(
loaders=[LocalSkills("/path/to/skills")]
)
)
# Agent 只在需要时加载相关技能
2. 可重用的知识包(Reusable Knowledge Packages)
一次创建,跨多个 Agent 使用。例如:
- 代码审查技能可以在调试 Agent、PR 审查 Agent 和代码生成 Agent 之间共享
- 数据分析技能可以在报表生成、趋势预测等多个场景中复用
3. 渐进式发现(Progressive Discovery)
Skills 使用懒加载来保持上下文窗口高效:
┌─────────────────────────────────────────────────────────┐
│ 1. Browse(浏览) │
│ Agent 在系统提示中看到技能摘要 │
│ 例:code-review: 代码审查辅助,包括样式检查 │
├─────────────────────────────────────────────────────────┤
│ 2. Load(加载) │
│ 当任务匹配时,Agent 调用 get_skill_instructions() │
│ 加载完整的代码审查指导 │
├─────────────────────────────────────────────────────────┤
│ 3. Reference(参考) │
│ Agent 调用 get_skill_reference() 访问详细文档 │
│ 例:加载 Python 样式指南 │
├─────────────────────────────────────────────────────────┤
│ 4. Execute(执行) │
│ Agent 调用 get_skill_script() 运行脚本 │
│ 例:执行代码风格检查脚本 │
└─────────────────────────────────────────────────────────┘
1.3 Skill 的架构
技能目录结构
一个标准的 Skill 包含以下三个部分:
code-review/ # 技能根目录
├── SKILL.md # 核心指令文件(必需)
├── scripts/ # 脚本目录(可选)
│ ├── check_style.py # Python 脚本
│ └── lint.sh # Shell 脚本
└── references/ # 参考文档目录(可选)
├── style-guide.md # 样式指南
└── best-practices.md # 最佳实践
SKILL.md 文件结构
SKILL.md 是技能的核心,采用 YAML frontmatter + Markdown 内容 的格式:
---
name: code-review # 必需:技能名称
description: 代码审查辅助,包括样式检查和最佳实践 # 必需:简短描述
license: Apache-2.0 # 可选:许可证
metadata: # 可选:元数据
version: "1.0.0"
author: your-name
tags: ["python", "code-quality"]
---
# Code Review Skill
在审查代码质量、样式和最佳实践时使用此技能。
## 何时使用
- 用户要求代码审查或反馈
- 用户想要提高代码质量
- 用户需要重构帮助
## 过程
1. **分析结构**:审查整体代码组织
2. **检查样式**:查找样式指南违规
3. **识别问题**:发现错误、安全问题、性能问题
4. **建议改进**:提供可操作的建议
## 最佳实践
- 首先关注最有影响力的问题
- 解释建议背后的"为什么"
- 为修复提供代码示例
字段说明
| 字段 | 类型 | 必需 | 说明 | 限制 |
|---|---|---|---|---|
name | string | ✅ | 技能名称 | 最多64字符,小写字母数字+连字符,与目录名匹配 |
description | string | ✅ | 技能描述 | 最多1024字符,显示在Agent系统提示中 |
license | string | ❌ | 许可证 | SPDX 标识符(MIT、Apache-2.0等) |
metadata | object | ❌ | 自定义元数据 | 可包含 version、author、tags 等 |
1.4 Skill 的工作原理
加载和发现机制
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.skills import Skills, LocalSkills
# 1. 创建 Skills 协调器,指定加载器
skills = Skills(
loaders=[
LocalSkills("/path/to/shared-skills"),
LocalSkills("/path/to/project-skills"),
]
)
# 2. Agent 初始化时自动注册技能工具
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
skills=skills
)
加载过程:
- 扫描目录:
LocalSkills扫描指定目录,查找包含SKILL.md的子目录 - 解析元数据:读取 YAML frontmatter,验证名称和描述
- 索引资源:记录 scripts 和 references 目录中的文件
- 注入系统提示:将技能名称和描述添加到 Agent 的系统消息中
- 注册工具:自动为 Agent 添加三个技能工具
技能工具
当向 Agent 添加技能时,它会自动获得以下工具:
| 工具 | 功能 | 参数 |
|---|---|---|
get_skill_instructions(skill_name) | 加载技能的完整指令(SKILL.md的内容) | skill_name: 技能名称 |
get_skill_reference(skill_name, reference_path) | 加载参考文档 | skill_name: 技能名称reference_path: 文档路径 |
get_skill_script(skill_name, script_path, execute, args, timeout) | 读取或执行脚本 | skill_name: 技能名称script_path: 脚本路径execute: 是否执行(布尔值)args: 脚本参数(列表)timeout: 超时时间(秒) |
执行流程
用户请求
↓
Agent 分析任务
↓
在系统提示中浏览可用技能
↓
判断是否需要某个技能
↓
调用 get_skill_instructions() ──→ 加载完整指令
↓
按照指令处理任务
↓
(可选)调用 get_skill_reference() ──→ 查阅参考文档
↓
(可选)调用 get_skill_script() ──→ 执行脚本
↓
返回结果给用户
二、Skill 使用
2.1 创建自定义 Skill
步骤 1:创建目录结构
# 创建技能根目录
mkdir -p my-skills/code-review
cd my-skills/code-review
# 创建子目录
mkdir scripts references
步骤 2:编写 SKILL.md
创建 SKILL.md 文件:
---
name: code-review
description: 代码审查辅助,包括样式检查和最佳实践
license: Apache-2.0
metadata:
version: "1.0.0"
author: your-name
tags: ["python", "code-quality"]
---
# Code Review Skill
在审查代码质量、样式和最佳实践时使用此技能。
## 何时使用
- 用户要求代码审查或反馈
- 用户想要提高代码质量
- 用户需要重构帮助
## 审查流程
### 1. 分析结构
- 检查代码组织和模块化
- 评估函数和类的职责分离
- 验证命名约定的一致性
### 2. 检查样式
- 遵循 PEP 8 样式指南
- 检查行长度(最多 100 字符)
- 验证导入顺序和分组
### 3. 识别问题
- **安全性**:SQL 注入、XSS 漏洞
- **性能**:不必要的循环、重复计算
- **错误处理**:缺失的异常处理
- **可维护性**:过于复杂的逻辑
### 4. 提供建议
- 解释问题的影响
- 提供具体的改进方案
- 附带代码示例
## 最佳实践
1. **优先级排序**:首先关注安全性和正确性问题
2. **建设性反馈**:解释"为什么"而不仅仅是"什么"
3. **代码示例**:为每个建议提供改进后的代码
4. **保持友好**:用积极的语气提出改进建议
步骤 3:添加脚本(可选)
创建 scripts/check_style.py:
#!/usr/bin/env python3
"""检查代码样式并返回结果。"""
import sys
import json
def check_style(code: str) -> dict:
"""检查代码样式问题。
Args:
code: 要检查的 Python 代码
Returns:
包含问题列表的字典
"""
issues = []
lines = code.split('\n')
for i, line in enumerate(lines, 1):
# 检查行长度
if len(line) > 100:
issues.append({
"line": i,
"type": "line_length",
"message": f"Line {i} exceeds 100 characters ({len(line)} chars)"
})
# 检查尾随空格
if line.endswith(' ') and line.strip():
issues.append({
"line": i,
"type": "trailing_whitespace",
"message": f"Line {i} has trailing whitespace"
})
# 检查制表符
if '\t' in line:
issues.append({
"line": i,
"type": "tab_character",
"message": f"Line {i} contains tab character (use spaces)"
})
return {
"total_issues": len(issues),
"issues": issues,
"summary": f"Found {len(issues)} style issue(s)"
}
if __name__ == "__main__":
# 从 stdin 或参数读取代码
if len(sys.argv) > 1:
code = sys.argv[1]
else:
code = sys.stdin.read()
result = check_style(code)
print(json.dumps(result, indent=2))
确保脚本有执行权限:
chmod +x scripts/check_style.py
步骤 4:添加参考文档(可选)
创建 references/style-guide.md:
# Python 样式指南
## 命名约定
### 变量和函数
- 使用 `snake_case`
- 使用描述性名称
- 避免单字母变量(除了循环计数器)
```python
# ✅ 好的命名
user_count = 10
def calculate_total_price(items):
pass
# ❌ 不好的命名
uc = 10
def calc(x):
pass
```
### 类
- 使用 PascalCase
- 名称应该是名词
```python
# ✅ 好的命名
class UserManager:
pass
# ❌ 不好的命名
class user_manager:
pass
```
### 常量
- 使用 `UPPER_SNAKE_CASE`
```python
# ✅ 好的命名
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30
# ❌ 不好的命名
maxConnections = 100
```
## 代码布局
### 行长度
- 每行最多 100 个字符
- 在逻辑点处断开长行
### 导入
```python
# 标准库导入
import os
import sys
# 第三方导入
import numpy as np
import pandas as pd
# 本地导入
from myapp.models import User
from myapp.utils import helper
```
### 空行
- 顶层函数和类之间用 2 个空行
- 类方法之间用 1 个空行
## 注释
### 文档字符串
```python
def calculate_discount(price: float, discount_rate: float) -> float:
"""计算折扣后的价格。
Args:
price: 原价
discount_rate: 折扣率(0-1之间)
Returns:
折扣后的价格
Raises:
ValueError: 如果 discount_rate 不在有效范围内
"""
if not 0 <= discount_rate <= 1:
raise ValueError("Discount rate must be between 0 and 1")
return price * (1 - discount_rate)
```
### 内联注释
- 使用注释解释"为什么"而不是"什么"
- 与代码保持一个空格
```python
# ✅ 好的注释
# 使用缓存避免重复的数据库查询
cached_user = cache.get(user_id)
# ❌ 不好的注释
# 从缓存获取用户
cached_user = cache.get(user_id)
验证规则
创建技能时,确保遵循以下规则:
名称要求:
- ✅
code-review - ✅
git-workflow - ✅
data-analysis-v2 - ❌
Code-Review(不能有大写) - ❌
-code-review(不能以连字符开头) - ❌
code--review(不能有连续连字符) - ❌
my_skill(不能使用下划线)
字段限制:
name:最多 64 个字符description:最多 1024 个字符
目录名称匹配: 技能目录名必须与 SKILL.md 中的 name 字段完全一致。
2.2 加载和使用 Skill
基本用法
from agno.agent import Agent
from agno.models.deepseek import DeepSeek
from agno.skills import Skills, LocalSkills
from dotenv import load_dotenv
load_dotenv()
# 从目录加载 skills
agent = Agent(
model=DeepSeek(),
skills=Skills(
loaders=[LocalSkills("path/skills/code-review")]
),
instructions=[
"你可以获得专业技能。",
"在需要时使用get_skill_instructions来加载完整的指导",
],
)
# Agent 将在相关时自动使用技能
agent.print_response(
"查看以下代码以获得最佳实践: def foo(): pass"
)
从技能目录加载
如果在子目录中有多个技能:
skills/
├── code-review/
│ └── SKILL.md
├── git-workflow/
│ └── SKILL.md
└── testing/
└── SKILL.md
from agno.skills import Skills, LocalSkills
# 从目录加载所有技能
skills = Skills(
loaders=[LocalSkills("/path/to/skills")]
)
加载单个技能
如果只想加载一个技能:
from agno.skills import Skills, LocalSkills
# 加载单个技能目录
skills = Skills(
loaders=[LocalSkills("/path/to/skills/code-review")]
)
多个加载器
可以组合多个加载器从不同位置加载技能:
from agno.skills import Skills, LocalSkills
skills = Skills(
loaders=[
LocalSkills("/path/to/shared-skills"), # 共享技能
LocalSkills("/path/to/project-skills"), # 项目特定技能
]
)
注意:如果来自不同加载器的技能具有相同名称,后面加载器的技能将覆盖前面的技能。
重新加载技能
如果技能在运行时发生变化,可以重新加载:
from agno.skills import Skills, LocalSkills
skills = Skills(
loaders=[LocalSkills("/path/to/skills")]
)
# ... 技能在磁盘上被修改 ...
# 重新加载以获取更改
skills.reload()
错误处理
技能在加载时会被验证。如果验证失败,会引发 SkillValidationError:
from agno.skills import Skills, LocalSkills, SkillValidationError
try:
skills = Skills(
loaders=[LocalSkills("/path/to/skills")]
)
except SkillValidationError as e:
print(f"Skill validation failed: {e}")
print(f"Errors: {e.errors}")
2.3 内置 Skill(Claude Agent Skills)
Agno 集成了 Claude Agent Skills,这是 Anthropic 提供的官方技能,允许 Claude Agent 访问文件系统资源并创建各类文档。
可用的 Claude Agent Skills
| Skill ID | 描述 | 功能 |
|---|---|---|
pptx | PowerPoint 技能 | 创建专业演示文稿,包括幻灯片、布局和格式 |
xlsx | Excel 技能 | 生成带有公式、图表和数据分析功能的电子表格 |
docx | Word 技能 | 创建和编辑格式丰富的文档 |
pdf | PDF 技能 | 分析和提取 PDF 文档中的信息 |
启用 Claude Agent Skills
from agno.agent import Agent
from agno.models.anthropic import Claude
# 启用单个技能
agent = Agent(
model=Claude(
id="claude-sonnet-4-5-20250929",
skills=[
{"type": "anthropic", "skill_id": "pptx", "version": "latest"}
]
),
instructions=["你是一个演讲专家。"],
markdown=True
)
agent.print_response("制作一份关于人工智能趋势的3张幻灯片")
同时启用多个技能
from agno.models.anthropic import Claude
model = Claude(
id="claude-sonnet-4-5-20250929",
skills=[
{"type": "anthropic", "skill_id": "pptx", "version": "latest"},
{"type": "anthropic", "skill_id": "xlsx", "version": "latest"},
{"type": "anthropic", "skill_id": "docx", "version": "latest"},
]
)
文件下载
由 Claude Skills 创建的文件存储在沙盒环境中,需要使用 Anthropic Files API 进行下载。
创建 file_download_helper.py:
"""辅助脚本:下载 Claude Agent Skills 生成的文件。"""
import os
from pathlib import Path
from typing import List
from anthropic import Anthropic
def download_skill_files(
provider_data: dict,
client: Anthropic,
output_dir: str = "downloads",
default_filename: str = "output"
) -> List[str]:
"""下载技能生成的文件。
Args:
provider_data: Agent 响应中的 provider_data
client: Anthropic 客户端实例
output_dir: 输出目录
default_filename: 默认文件名
Returns:
下载的文件路径列表
"""
# 创建输出目录
Path(output_dir).mkdir(parents=True, exist_ok=True)
downloaded_files = []
# 从 provider_data 提取文件信息
if "skill_results" in provider_data:
for result in provider_data["skill_results"]:
if "files" in result:
for file_info in result["files"]:
file_id = file_info.get("file_id")
filename = file_info.get("filename", default_filename)
if file_id:
# 使用 Anthropic API 下载文件
file_content = client.files.content(file_id)
# 保存到本地
output_path = os.path.join(output_dir, filename)
with open(output_path, "wb") as f:
f.write(file_content.read())
downloaded_files.append(output_path)
return downloaded_files
使用示例:
import os
from agno.agent import Agent
from agno.models.anthropic import Claude
from anthropic import Anthropic
from file_download_helper import download_skill_files
# 创建 Agent
agent = Agent(
model=Claude(
id="claude-sonnet-4-5-20250929",
skills=[{"type": "anthropic", "skill_id": "pptx", "version": "latest"}]
),
markdown=True
)
# 生成演示文稿
response = agent.run("Create a presentation about Python best practices")
# 下载生成的文件
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
if response.messages:
for msg in response.messages:
if hasattr(msg, "provider_data") and msg.provider_data:
files = download_skill_files(
msg.provider_data,
client,
default_filename="python_best_practices.pptx"
)
if files:
print(f"Downloaded {len(files)} file(s):")
for file in files:
print(f" - {file}")
2.4 Skill 参数配置
Skills 类参数
from agno.skills import Skills, LocalSkills
skills = Skills(
loaders=[
LocalSkills(path="/path/to/skills"),
# 可以添加多个加载器
],
# 未来可能支持的配置选项
# cache_enabled=True, # 是否缓存技能内容
# reload_on_change=False, # 是否监控文件变化自动重载
)
Agent 与 Skills 集成
from agno.agent import Agent
from agno.models.deepseek import DeepSeek
from agno.skills import Skills, LocalSkills
from dotenv import load_dotenv
load_dotenv()
# 获取相对于此文件的技能目录
agent = Agent(
name="Code Assistant",
model=DeepSeek(),
skills=Skills(
loaders=[LocalSkills("path/skills/code-review")]
),
instructions=[
"你是一个有用的编码助手,拥有专门的技能。",
"当用户要求代码审查时,加载代码审查技能。",
"总是在你的建议中提供清晰的解释。",
],
read_tool_call_history=True, # 显示工具调用(调试用)
markdown=True, # 启用 Markdown 格式输出
)
# 使用 Agent
if __name__ == "__main__":
agent.print_response(
"审查这个Python函数:\n\n"
"def calc(x,y): return x+y"
)
三、实践案例
案例一:代码审查技能
场景:构建一个代码审查助手,能够自动检查代码质量、样式问题并提供改进建议。
步骤 1:创建技能结构
mkdir -p skills/code-review/scripts
mkdir -p skills/code-review/references
步骤 2:编写 SKILL.md
skills/code-review/SKILL.md:
---
name: code-review
description: 全面的代码审查助手,检查样式、安全性、性能和最佳实践
license: MIT
metadata:
version: "1.0.0"
author: agno-tutorial
tags: ["python", "code-quality", "linting"]
---
# Code Review Skill
专业的代码审查助手,提供全面的代码质量分析。
## 何时使用
- 用户提交代码需要审查
- 用户询问代码改进建议
- 需要检查代码是否符合最佳实践
## 审查维度
### 1. 代码风格(Style)
- PEP 8 合规性检查
- 命名约定一致性
- 代码格式和缩进
### 2. 安全性(Security)
- SQL 注入风险
- XSS 漏洞
- 敏感信息泄露
### 3. 性能(Performance)
- 算法复杂度
- 不必要的重复计算
- 内存使用优化
### 4. 可维护性(Maintainability)
- 函数长度和复杂度
- 代码重复(DRY 原则)
- 注释和文档
## 审查流程
1. **快速扫描**:识别明显问题
2. **使用脚本**:运行 `check_style.py` 进行自动检查
3. **深入分析**:手动审查逻辑和架构
4. **生成报告**:按优先级列出问题和建议
## 输出格式
```markdown
## 代码审查报告
### ⚠️ 关键问题(Critical)
- [问题描述]
- 位置:第 X 行
- 建议:[具体改进方案]
- 示例:[改进后的代码]
### ⚡ 性能问题(Performance)
- ...
### 📝 样式建议(Style)
- ...
### ✨ 最佳实践(Best Practice)
- ...
相关资源
- 参考:
style-guide.md- Python 样式指南 - 脚本:
check_style.py- 自动样式检查
步骤 3:创建检查脚本
skills/code-review/scripts/check_style.py:
#!/usr/bin/env python3
"""
代码样式检查脚本
检查常见的 Python 代码样式问题
"""
import sys
import json
import re
from typing import List, Dict
def check_style(code: str) -> Dict:
"""执行代码样式检查。
Args:
code: 要检查的 Python 代码
Returns:
包含问题分类的检查结果
"""
issues = {
"critical": [],
"style": [],
"best_practice": []
}
lines = code.split('\n')
for i, line in enumerate(lines, 1):
# 检查行长度
if len(line) > 100:
issues["style"].append({
"line": i,
"message": f"Line exceeds 100 characters ({len(line)} chars)",
"suggestion": "Break long lines at logical points"
})
# 检查尾随空格
if line.endswith(' ') and line.strip():
issues["style"].append({
"line": i,
"message": "Trailing whitespace",
"suggestion": "Remove trailing spaces"
})
# 检查制表符
if '\t' in line:
issues["style"].append({
"line": i,
"message": "Tab character found",
"suggestion": "Use 4 spaces instead of tabs"
})
# 检查 print 语句(可能是调试代码)
if re.search(r'\bprint\s*(', line):
issues["best_practice"].append({
"line": i,
"message": "print() statement found",
"suggestion": "Consider using logging instead of print for production code"
})
# 检查裸 except
if re.search(r'except\s*:', line):
issues["critical"].append({
"line": i,
"message": "Bare except clause",
"suggestion": "Specify exception types: except ValueError:"
})
# 检查 TODO 注释
if 'TODO' in line or 'FIXME' in line:
issues["best_practice"].append({
"line": i,
"message": "TODO/FIXME comment found",
"suggestion": "Address pending tasks before code review"
})
# 统计信息
total = sum(len(v) for v in issues.values())
return {
"total_issues": total,
"issues": issues,
"summary": {
"critical": len(issues["critical"]),
"style": len(issues["style"]),
"best_practice": len(issues["best_practice"])
}
}
def main():
# 从命令行参数或 stdin 读取代码
if len(sys.argv) > 1:
code = sys.argv[1]
else:
code = sys.stdin.read()
# 执行检查
result = check_style(code)
# 输出 JSON 格式结果
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
chmod +x skills/code-review/scripts/check_style.py
步骤 4:创建参考文档
skills/code-review/references/style-guide.md:
# Python 代码审查样式指南
## 命名约定
### 变量和函数:snake_case
```python
# ✅ 正确
user_count = 10
def calculate_total():
pass
# ❌ 错误
userCount = 10
def CalculateTotal():
pass
```
### 类:PascalCase
```python
# ✅ 正确
class UserManager:
pass
# ❌ 错误
class user_manager:
pass
```
### 常量:UPPER_SNAKE_CASE
```python
# ✅ 正确
MAX_SIZE = 100
# ❌ 错误
maxSize = 100
```
## 代码布局
### 行长度
- 最大 100 字符
- 在逻辑断点处换行
### 空行
- 顶层定义之间:2 个空行
- 方法之间:1 个空行
### 导入顺序
```python
# 1. 标准库
import os
import sys
# 2. 第三方库
import numpy as np
# 3. 本地模块
from myapp import models
```
## 最佳实践
### 异常处理
```python
# ✅ 正确:指定异常类型
try:
value = int(user_input)
except ValueError:
print("Invalid input")
# ❌ 错误:裸 except
try:
value = int(user_input)
except:
print("Error")
```
### 使用 logging 而非 print
```python
# ✅ 正确
import logging
logging.info("Processing started")
# ❌ 错误(生产代码中)
print("Processing started")
```
### 文档字符串
```python
def calculate_discount(price: float, rate: float) -> float:
"""计算折扣后的价格。
Args:
price: 原始价格
rate: 折扣率(0-1)
Returns:
折扣后的价格
Raises:
ValueError: 如果 rate 不在有效范围内
"""
if not 0 <= rate <= 1:
raise ValueError("Rate must be between 0 and 1")
return price * (1 - rate)
```
步骤 5:创建 Agent
code_review_agent.py:
"""代码审查助手 Agent"""
from pathlib import Path
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.skills import Skills, LocalSkills
# 技能目录
skills_dir = Path(__file__).parent / "skills"
# 创建代码审查 Agent
code_reviewer = Agent(
name="Code Reviewer",
model=OpenAIChat(id="gpt-4o"),
skills=Skills(
loaders=[LocalSkills(str(skills_dir))]
),
instructions=[
"You are an expert code reviewer with access to the code-review skill.",
"When reviewing code:",
"1. First, use get_skill_script to run check_style.py for automated checks",
"2. Then, use get_skill_instructions to load the full review guidelines",
"3. Perform manual analysis for logic, security, and performance issues",
"4. Refer to style-guide.md when needed",
"5. Present findings in a clear, prioritized format",
"Always be constructive and provide specific, actionable suggestions."
],
show_tool_calls=True,
markdown=True,
)
def main():
print("=== Code Review Assistant ===\n")
# 示例代码
sample_code = '''
def calc(x,y):
print("Calculating...")
try:
result=x/y
except:
print("Error")
return None
return result
userInput = input("Enter number: ")
answer = calc(10, int(userInput))
print(answer)
'''
print("📝 Code to review:")
print(sample_code)
print("\n" + "="*50 + "\n")
# 执行代码审查
code_reviewer.print_response(
f"Please review the following Python code and provide detailed feedback:\n\n```python\n{sample_code}\n```",
stream=True
)
if __name__ == "__main__":
main()
步骤 6:运行和测试
# 安装依赖
pip install agno openai
# 设置 API 密钥
export OPENAI_API_KEY="your-api-key"
# 运行
python code_review_agent.py
预期输出:
=== Code Review Assistant ===
📝 Code to review:
def calc(x,y):
print("Calculating...")
...
==================================================
🔧 Running tool: get_skill_script
- skill_name: code-review
- script_path: scripts/check_style.py
- execute: True
📊 Automated Check Results:
{
"total_issues": 8,
"issues": {
"critical": [
{
"line": 5,
"message": "Bare except clause",
"suggestion": "Specify exception types: except ZeroDivisionError:"
}
],
"style": [...],
"best_practice": [...]
}
}
## 代码审查报告
### ⚠️ 关键问题(Critical)
1. **裸 except 子句**(第 5 行)
- 问题:捕获所有异常会隐藏潜在错误
- 建议:指定具体的异常类型
```python
try:
result = x / y
except ZeroDivisionError:
print("Cannot divide by zero")
return None
```
### 📝 样式问题(Style)
1. **函数定义缺少空格**(第 1 行)
- 当前:`def calc(x,y):`
- 建议:`def calc(x, y):`
2. **变量命名不符合规范**(第 10 行)
- 当前:`userInput`
- 建议:`user_input`(使用 snake_case)
### ⚡ 性能问题(Performance)
无明显性能问题。
### ✨ 最佳实践建议
1. **使用 logging 替代 print**
```python
import logging
logging.info("Calculating...")
```
2. **添加类型提示**
```python
def calc(x: float, y: float) -> float | None:
...
```
3. **添加文档字符串**
```python
def calc(x: float, y: float) -> float | None:
"""计算 x 除以 y 的结果。
Args:
x: 被除数
y: 除数
Returns:
计算结果,如果除法失败则返回 None
"""
```
### 📋 改进后的完整代码
```python
import logging
def calculate_division(dividend: float, divisor: float) -> float | None:
"""计算两个数的除法。
Args:
dividend: 被除数
divisor: 除数
Returns:
计算结果,如果除数为零则返回 None
"""
logging.info("Performing division calculation")
try:
result = dividend / divisor
except ZeroDivisionError:
logging.error("Cannot divide by zero")
return None
return result
# 主程序
if __name__ == "__main__":
user_input = input("Enter a number: ")
try:
divisor = float(user_input)
answer = calculate_division(10, divisor)
if answer is not None:
print(f"Result: {answer}")
except ValueError:
print("Invalid input: please enter a valid number")
案例二:智能文档助手
场景:构建一个文档助手,能够理解 Agno 框架文档并回答用户问题。
步骤 1:创建技能
skills/agno-docs/SKILL.md:
---
name: agno-docs
description: Agno 框架文档专家,提供关于 Agent、Skills、Tools 的帮助
license: MIT
metadata:
version: "1.0.0"
tags: ["documentation", "agno", "help"]
---
# Agno Documentation Skill
提供 Agno 框架的专业文档支持和技术指导。
## 何时使用
- 用户询问 Agno 框架的使用方法
- 需要查找特定 API 或功能的文档
- 用户需要代码示例或最佳实践
## 知识范围
### Agent
- Agent 的创建和配置
- 模型选择和参数设置
- 指令编写技巧
### Skills
- Skills 的定义和结构
- 加载和使用 Skills
- 自定义 Skills 开发
### Tools
- 内置工具使用
- 自定义工具开发
- 工具组合和编排
## 回答原则
1. **准确性**:基于官方文档提供准确信息
2. **完整性**:提供完整的代码示例
3. **实用性**:关注实际应用场景
4. **渐进性**:从简单到复杂逐步讲解
步骤 2:添加参考文档
skills/agno-docs/references/agent-guide.md:
# Agent 开发指南
## 创建基础 Agent
```python
from agno.agent import Agent
from agno.models.openai import OpenAIChat
agent = Agent(
name="My Agent",
model=OpenAIChat(id="gpt-4o"),
instructions=["You are a helpful assistant."],
markdown=True
)
agent.print_response("Hello!")
Agent 配置参数
核心参数
| 参数 | 类型 | 说明 |
|---|---|---|
name | str | Agent 名称 |
model | Model | 使用的 LLM 模型 |
instructions | List[str] | 指令列表 |
tools | List[Tool] | 可用工具列表 |
skills | Skills | 技能配置 |
记忆和存储
from agno.storage.agent.sqlite import SqliteAgentStorage
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
storage=SqliteAgentStorage(
table_name="sessions",
db_file="agent_storage.db"
),
add_history_to_messages=True,
num_history_responses=3,
session_id="user_123"
)
知识库集成
from agno.knowledge.pdf_url import PDFUrlKnowledgeBase
from agno.vectordb.lancedb import LanceDb
knowledge = PDFUrlKnowledgeBase(
urls=["https://example.com/docs.pdf"],
vector_db=LanceDb(
table_name="docs",
uri="tmp/lancedb"
)
)
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
knowledge=knowledge
)
步骤 3:创建文档助手 Agent
docs_assistant.py:
"""Agno 文档助手"""
from pathlib import Path
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.skills import Skills, LocalSkills
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.storage.agent.sqlite import SqliteAgentStorage
# 技能目录
skills_dir = Path(__file__).parent / "skills"
# 创建文档助手
docs_assistant = Agent(
name="Agno Docs Assistant",
model=OpenAIChat(id="gpt-4o"),
# 加载文档技能
skills=Skills(
loaders=[LocalSkills(str(skills_dir / "agno-docs"))]
),
# 添加网页搜索能力(用于查找最新信息)
tools=[DuckDuckGoTools()],
# 配置记忆
storage=SqliteAgentStorage(
table_name="docs_sessions",
db_file="tmp/docs_assistant.db"
),
add_history_to_messages=True,
num_history_responses=5,
instructions=[
"You are an expert on the Agno framework.",
"Use the agno-docs skill to provide accurate documentation.",
"When answering questions:",
"1. Load the relevant reference document using get_skill_reference",
"2. Provide complete, runnable code examples",
"3. Explain concepts clearly with step-by-step instructions",
"4. If information is not in the docs, use DuckDuckGo to search",
"Always cite your sources and indicate when using external information."
],
show_tool_calls=True,
markdown=True,
)
def interactive_mode():
"""交互式问答模式"""
print("=== Agno Documentation Assistant ===")
print("Ask me anything about the Agno framework!")
print("Type 'exit' to quit.\n")
session_id = "docs_session_001"
docs_assistant.session_id = session_id
while True:
try:
question = input("\n🤔 You: ").strip()
if question.lower() in ['exit', 'quit', 'bye']:
print("\n👋 Goodbye!")
break
if not question:
continue
print(f"\n🤖 Assistant:")
docs_assistant.print_response(question, stream=True)
except KeyboardInterrupt:
print("\n\n👋 Goodbye!")
break
except Exception as e:
print(f"\n❌ Error: {e}")
def main():
# 示例问题
examples = [
"How do I create an Agent with memory?",
"What is the difference between Skills and Tools?",
"Show me how to integrate a knowledge base with an Agent"
]
print("=== Agno Documentation Assistant ===\n")
print("Example questions:")
for i, q in enumerate(examples, 1):
print(f"{i}. {q}")
print("\n" + "="*60 + "\n")
# 回答第一个问题
print("📚 Answering: How do I create an Agent with memory?\n")
docs_assistant.print_response(
"How do I create an Agent with memory? Show me a complete example.",
stream=True
)
print("\n" + "="*60 + "\n")
# 启动交互模式
choice = input("Start interactive mode? (y/n): ").strip().lower()
if choice == 'y':
interactive_mode()
if __name__ == "__main__":
main()
步骤 4:运行和测试
python docs_assistant.py
示例对话:
=== Agno Documentation Assistant ===
📚 Answering: How do I create an Agent with memory?
🔧 Running tool: get_skill_reference
- skill_name: agno-docs
- reference_path: references/agent-guide.md
🤖 Assistant:
要创建具有记忆功能的 Agent,您需要配置存储后端和会话管理。以下是完整示例:
## 方法一:使用 SQLite 存储
```python
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.storage.agent.sqlite import SqliteAgentStorage
agent = Agent(
name="Memory Agent",
model=OpenAIChat(id="gpt-4o"),
# 配置 SQLite 存储
storage=SqliteAgentStorage(
table_name="agent_sessions", # 表名
db_file="tmp/agent_storage.db" # 数据库文件路径
),
# 启用历史消息
add_history_to_messages=True, # 将历史添加到上下文
num_history_responses=3, # 保留最近 3 轮对话
# 设置会话 ID
session_id="user_session_001", # 唯一会话标识符
markdown=True
)
# 第一轮对话
agent.print_response("I love Python programming.")
# 第二轮对话(Agent 会记住之前的内容)
agent.print_response("What did I just say I love?")
# Agent 回复:You mentioned that you love Python programming.
方法二:使用 PostgreSQL 存储
from agno.storage.agent.postgres import PostgresAgentStorage
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
storage=PostgresAgentStorage(
table_name="agent_sessions",
db_url="postgresql://user:password@localhost:5432/dbname"
),
add_history_to_messages=True,
session_id="user_session_001"
)
关键参数说明
- storage: 存储后端,用于持久化对话历史
- add_history_to_messages: 是否将历史消息添加到上下文
- num_history_responses: 保留多少轮历史对话(控制上下文长度)
- session_id: 会话唯一标识符,用于区分不同用户或对话
最佳实践
-
为每个用户使用唯一的 session_id
session_id = f"user_{user_id}_{datetime.now().isoformat()}" -
控制历史消息数量以避免上下文过载
num_history_responses=5 # 通常 3-10 轮比较合适 -
定期清理旧会话数据
# 删除 30 天前的会话 storage.delete_old_sessions(days=30)
这样,您的 Agent 就具备了跨对话的记忆能力!
Skills vs Tools
| 特性 | Skills | Tools |
|---|---|---|
| 用途 | 提供领域专业知识和指导 | 执行具体的操作 |
| 内容 | 指令、参考文档、脚本 | 可调用的函数 |
| 加载 | 按需加载(懒加载) | 总是可用 |
| 例子 | 代码审查指南、写作风格 | 网页搜索、数据库查询 |
组合使用示例
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.skills import Skills, LocalSkills
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.tools.file import FileTools
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
# 添加 Skills(知识和指导)
skills=Skills(
loaders=[
LocalSkills("skills/code-review"),
LocalSkills("skills/documentation")
]
),
# 添加 Tools(操作能力)
tools=[
DuckDuckGoTools(), # 网页搜索
FileTools(), # 文件操作
],
instructions=[
"Use skills for guidance on how to approach tasks.",
"Use tools to perform actual operations.",
"Combine both for comprehensive solutions."
]
)
实际应用场景
场景:代码审查助手
# Agent 的工作流程:
# 1. 用户提交代码
# 2. 使用 code-review skill 加载审查指南
# 3. 使用 file tool 读取代码文件
# 4. 使用 skill 中的脚本执行自动检查
# 5. 使用 web search tool 查找最佳实践
# 6. 生成综合报告
agent.print_response("Review the file main.py")
# Agent 会:
# - get_skill_instructions("code-review") → 获取审查指南
# - FileTools.read_file("main.py") → 读取文件
# - get_skill_script("code-review", "check_style.py", execute=True) → 运行检查
# - DuckDuckGoTools.search("Python best practices 2024") → 搜索最新实践
Skills 提供"知道如何做"的知识,Tools 提供"能够做"的能力。
四、最佳实践建议
1. Skill 设计原则
✅ DO(推荐做法)
单一职责原则
✅ 好的设计:
skills/
├── code-review/ # 专注于代码审查
├── git-workflow/ # 专注于 Git 工作流
└── testing/ # 专注于测试策略
❌ 不好的设计:
skills/
└── programming/ # 太宽泛,包含所有编程相关内容
清晰的描述
# ✅ 好的描述
description: 代码审查辅助,检查 Python 代码的样式、安全性和性能问题
# ❌ 不好的描述
description: 帮助编程
结构化的指令
✅ 好的指令:
## 何时使用
- 明确的使用场景
## 过程
1. 步骤一
2. 步骤二
3. 步骤三
## 最佳实践
- 具体的建议
❌ 不好的指令:
随便写一些关于代码审查的内容...
❌ DON'T(避免的做法)
-
不要创建过于宽泛的技能
- ❌ "programming-helper"(太宽泛)
- ✅ "python-code-review"、"git-workflow"(具体明确)
-
不要在 SKILL.md 中放置大量代码
- ❌ 在 SKILL.md 中包含数百行代码示例
- ✅ 使用 scripts/ 目录存放脚本,references/ 存放详细文档
-
不要忽略验证规则
- ❌ 技能名称使用大写字母或下划线
- ✅ 使用小写字母、数字和连字符
2. 开发工作流最佳实践
本地开发和测试
# 开发模式配置
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
skills=Skills(loaders=[LocalSkills("./skills")]),
show_tool_calls=True, # ✅ 显示工具调用
debug_mode=True, # ✅ 启用调试模式
markdown=True,
)
# 测试技能加载
try:
skills = Skills(loaders=[LocalSkills("./skills")])
print("✅ Skills loaded successfully")
print(f"Available skills: {skills.list()}")
except SkillValidationError as e:
print(f"❌ Validation failed: {e}")
版本控制
skills/
├── code-review/
│ ├── SKILL.md # ✅ 在 YAML 中包含版本号
│ ├── CHANGELOG.md # ✅ 记录变更历史
│ └── README.md # ✅ 使用说明
---
name: code-review
description: 代码审查辅助
metadata:
version: "1.2.0" # ✅ 语义化版本
changelog:
- "1.2.0: Added security checks"
- "1.1.0: Improved performance analysis"
- "1.0.0: Initial release"
---
3. 性能优化
控制技能数量
# ❌ 加载所有技能(如果有很多技能会影响性能)
skills = Skills(loaders=[
LocalSkills("/path/to/100-skills")
])
# ✅ 只加载需要的技能
skills = Skills(loaders=[
LocalSkills("/path/to/skills/code-review"),
LocalSkills("/path/to/skills/git-workflow"),
])
优化脚本执行
# scripts/check_style.py
# ✅ 设置合理的超时
agent.run("""
Use get_skill_script with:
- execute: True
- timeout: 30 # 30 秒超时
""")
# ✅ 缓存重复计算的结果
cache = {}
def expensive_check(code):
code_hash = hash(code)
if code_hash in cache:
return cache[code_hash]
result = perform_check(code)
cache[code_hash] = result
return result
按需加载参考文档
## Skill 指令设计
✅ 好的设计:
在 SKILL.md 中提供概要,在 references/ 中提供详细内容
❌ 不好的设计:
在 SKILL.md 中放置所有详细文档(会增加上下文长度)
4. 安全性考虑
脚本执行安全
# ✅ 验证脚本来源
def validate_skill_script(skill_path):
"""验证技能脚本的完整性"""
import hashlib
# 计算脚本哈希
with open(skill_path, 'rb') as f:
script_hash = hashlib.sha256(f.read()).hexdigest()
# 与已知安全哈希比对
known_hashes = {
"check_style.py": "abc123...",
}
return script_hash in known_hashes.values()
# ✅ 限制脚本权限
# 在 Docker 容器或沙盒环境中运行脚本
输入验证
# scripts/process_data.py
def process_data(user_input: str):
# ✅ 验证输入
if not user_input or len(user_input) > 10000:
raise ValueError("Invalid input length")
# ✅ 清理输入
clean_input = sanitize(user_input)
# ✅ 使用参数化查询(如果涉及数据库)
# 避免 SQL 注入
5. 文档和维护
编写清晰的文档
# ✅ 好的 Skill 文档结构
# Skill 名称
简短描述(1-2 句话)
## 何时使用
明确的使用场景列表
## 功能
- 功能 1
- 功能 2
## 使用示例
具体的使用例子
## 限制和注意事项
- 限制 1
- 限制 2
## 相关资源
- 脚本:xxx
- 参考:yyy
持续更新
# ✅ 提供 reload 机制
skills = Skills(loaders=[LocalSkills("./skills")])
# 技能更新后
skills.reload()
# ✅ 监控技能使用情况
from agno.monitoring import track_skill_usage
@track_skill_usage
def use_skill(agent, skill_name):
return agent.get_skill_instructions(skill_name)
6. 团队协作
Skill 共享
company-skills/ # 公司级共享技能
├── code-review/
├── documentation/
└── security-check/
project-skills/ # 项目特定技能
├── api-design/
└── database-migration/
# 加载时合并
skills = Skills(loaders=[
LocalSkills("company-skills"),
LocalSkills("project-skills"),
])
贡献指南
创建 CONTRIBUTING.md:
# Skill 贡献指南
## 提交新 Skill
1. 确保遵循命名规范
2. 包含完整的 SKILL.md
3. 添加测试用例
4. 更新 README
## 代码审查清单
- [ ] 名称符合规范
- [ ] 描述清晰准确
- [ ] 包含使用示例
- [ ] 脚本有执行权限
- [ ] 通过验证测试
7. 监控和调试
日志记录
import logging
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 在 Agent 中启用调试
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
skills=Skills(loaders=[LocalSkills("./skills")]),
debug_mode=True, # ✅ 启用调试模式
show_tool_calls=True, # ✅ 显示工具调用
)
# 记录技能使用
logger = logging.getLogger("skill_usage")
logger.info(f"Loaded skill: {skill_name}")
错误处理
from agno.skills import SkillValidationError
try:
skills = Skills(loaders=[LocalSkills("./skills")])
except SkillValidationError as e:
logger.error(f"Skill validation failed: {e}")
logger.error(f"Errors: {e.errors}")
# 降级处理:使用默认配置
skills = None
except Exception as e:
logger.exception(f"Unexpected error: {e}")
raise
总结
通过本教程,我们深入学习了 Agno Skills 的核心概念、使用方法和最佳实践:
关键要点
- Skill 是知识包:将领域专业知识打包成可重用的模块
- 渐进式发现:Browse → Load → Reference → Execute
- 三个核心组件:SKILL.md(指令)、scripts/(脚本)、references/(参考文档)
- 与 Tools 互补:Skills 提供"如何做"的知识,Tools 提供"能够做"的能力
- Claude Agent Skills:内置的文档处理能力(PPTX、XLSX、DOCX、PDF)