Agent Skills に対応した Strands Agents でスキルを試してみる

記事タイトルとURLをコピーする

はじめに

こんにちは、久保(賢)です。

Strands Agents SDK for Python が3/11にリリースされた v1.30.0 でAgent Skillsに対応しました。

Release v1.30.0 · strands-agents/sdk-python · GitHub

本記事では、Agent Skills の概要を整理したうえで、Strands Agents から Slack 用カスタム絵文字を生成するスキルを試した結果を紹介します。

Strands Agents とは

AWSが開発したオープンソースとして公開されている、AIエージェントのSDKです。

弊社の過去ブログもご参照ください。

blog.serverworks.co.jp

blog.serverworks.co.jp

Agent Skills とは

Agent Skillsは、Anthropicが開発しオープン標準として公開した、AIエージェントに特定の「知識」や「手順」をパッケージ化して提供するための仕組みです。

仕様はこちらで公開されています。

agentskills.io

中核となる SKILL.md には、Skillの名前や説明などのメタデータと実行指示を記述し、必要に応じてスクリプト、参考資料、テンプレートなどもまとめてパッケージ化できます。

Agent Skillsの特徴は progressive disclosure(段階的開示) にあります。エージェントは起動時に各Skillの名前と概要だけを読み、関連すると判断したときに初めて SKILL.md の詳細や関連ファイルを読み込みます。この設計により、必要なコンテキストだけを段階的に取り込めるため、トークン消費の抑制に役立ちます。

なお、MCPは外部のツールやデータソースへの接続を標準化する仕組みであり、Agent Skillsはエージェント側(ローカル)で使う知識や手順をパッケージ化する仕組み、と捉えると分かりやすいです。

仕様

仕様は非常にシンプルで、以下のように特定のスキル用のフォルダを作り、その中に SKILL.md を置くだけでスキルが定義できます。

my-skill/
├── SKILL.md            # 必須 — 概要 + 実行指示
├── scripts/            # オプション — 実行可能なスクリプト
├── references/         # オプション — 参考資料
└── assets/             # オプション — 静的ファイル(設定、データ)

SKILL.md の内容は以下のような構成になっています。
最低限、nameとdescription、最下部の実行指示を記載すればスキルとして機能します。
nameはスキルの名称、descriptionにスキルが何をするのか、そしていつ使うのかの説明を記載します。

---
name: slack-emoji-generator
description: >
  テキストやイニシャルからSlackカスタム絵文字用の画像を生成するスキル。
  「絵文字を作って」「Slack用のアイコンがほしい」「リアクションスタンプを作りたい」
  といったリクエストで起動する。
license: Apache-2.0                    # オプション
compatibility: Designed for Claude Code  # オプション
metadata:                              # オプション
  author: your-name
  version: "1.0"
allowed-tools: Bash(git:*) Bash(jq:*) Read   # オプション, スペース区切り
---

# Slack Emoji Generator スキル
  
Slackカスタム絵文字用の画像ファイルを生成するスキルです。
  

Agent Skillsに対応したエージェントでは、起動時にまずスキルのnameとdescriptionのみを読み込み、 動作中に適切なスキルを特定し、必要に応じてスキルの詳細やスクリプトといったスキル本体のデータを読み込みます。

Strands AgentsのAgent Skills対応

3/11にリリースされた v1.30.0 で Strands Agents SDK for Python で Agent Skillsに対応しました。

Release v1.30.0 · strands-agents/sdk-python · GitHub

※Strands Agents はTypeScriptのSDKも提供していますが、本記事執筆時点ではpython SDKのみの対応です。

Claude Agent SDK、CrewAI、Microsoft Agent Frameworkなど他のエージェントフレームワークもすでにAgent Skillsに対応しており、この度Strands Agentsでも利用可能となったようです。

Claude CodeやKiro、Codex、GitHub CopilotといったコーディングエージェントでもAgent Skillsは利用可能です。

Agent Skillsの利用方法

skills のとおり、以下のようにSkillをエージェントに組み込むことができます。

from strands import Agent, AgentSkills, Skill
  
# 単一のスキルディレクトリ — リストは不要
plugin = AgentSkills(skills="./skills/pdf-processing")
  
# 親ディレクトリ — SKILL.md を含む全ての子ディレクトリを読み込む
plugin = AgentSkills(skills="./skills/")
  
# 複数のソースを組み合わせる
plugin = AgentSkills(skills=[
    "./skills/pdf-processing",     # 単一のスキルディレクトリ
    "./skills/",                   # 親ディレクトリ(全ての子ディレクトリを読み込む)
    Skill(                         # プログラムで定義するスキル
        name="custom-greeting",
        description="カスタム挨拶を生成する",
        instructions="ユーザーを名前で呼んで、熱意を込めて挨拶する。",
    ),
])
  
agent = Agent(plugins=[plugin])

またSDKであることを活かして、SKILL.mdの内容をプログラムで動的に生成してからAgentSkillsに渡す、といったことも可能です。

from strands import Skill
  
skill = Skill(
    name="code-review",
    description="Review code for best practices and bugs",
    instructions="Review the provided code. Check for...",
)
  
skill = Skill.from_content("""---
name: code-review
description: Review code for best practices and bugs
---
Review the provided code. Check for...
""")

Strands AgentsでAgent Skillsを試してみる

Slack絵文字を生成するスキル

Agent SkillsはSKILL.mdさえあれば簡単にスキルを定義できますが、せっかくなのでscriptsやreferencesも活用してみたいと思い、Slack絵文字を生成するスキルを用意してみました。

スキルの構成

skills
└── slack-emoji-generator
    ├── references
    │   └── slack-emoji-spec.md
    ├── scripts
    │   └── generate_emoji.py
    └── SKILL.md

SKILL.md の内容は以下のとおりです。

SKILL.mdを表示する
---
name: slack-emoji-generator
description: >
  テキストやイニシャルからSlackカスタム絵文字用の画像を生成するスキル。
  「絵文字を作って」「Slack用のアイコンがほしい」「リアクションスタンプを作りたい」
  といったリクエストで起動する。
allowed-tools: file_read file_write shell
---
  
# Slack Emoji Generator スキル
  
Slackカスタム絵文字用の画像ファイルを生成するスキルです。
  
## ワークフロー
  
### Step 1: 要件の確認
ユーザーに以下を確認する(指定がなければデフォルトを使用):
- **表示テキスト**: 絵文字に入れる文字(例: "LGTM", "承認", "🔥")
- **背景色**: 色名または16進カラーコード(デフォルト: "#4A90D9")
- **文字色**: 色名または16進カラーコード(デフォルト: "white")
- **形状**: "circle"(丸)または "rounded"(角丸四角)(デフォルト: "rounded")
  
### Step 2: 画像生成
`scripts/generate_emoji.py` を使い、Slack仕様に準拠した画像を生成する。
  
\```bash
python scripts/generate_emoji.py --text "LGTM" --bg "#4CAF50" --fg "white" --shape rounded --output emoji_lgtm.png
\```
  
主なオプション:
- `--text`: 表示テキスト(必須)
- `--bg`: 背景色(デフォルト: "#4A90D9")
- `--fg`: 文字色(デフォルト: "white")
- `--shape`: "circle" or "rounded"(デフォルト: "rounded")
- `--output`: 出力ファイル名(デフォルト: emoji.png)
  
### Step 3: 仕様適合の確認
`references/slack-emoji-spec.md` を参照し、生成画像がSlackの仕様を満たしているか確認する。
スクリプトは自動的に 128×128px・PNG・128KB以下で出力するが、ユーザーに仕様情報を伝えると親切。
  
### 複数パターンの提案
ユーザーが迷っている場合は、色やテキストのバリエーションを数パターン生成して提案するとよい。

scripts/generate_emoji.pyの内容は以下のとおりです。
Pillowを使って、指定されたテキスト、背景色、文字色、形状で絵文字画像を生成するスクリプトになっています。

generate_emoji.pyを表示する
#!/usr/bin/env python3
"""
Slackカスタム絵文字用の画像を生成する。
  
Usage:
    python generate_emoji.py --text "LGTM" --bg "#4CAF50" --fg white --shape rounded --output emoji.png
  
Slack絵文字の仕様:
    - 128×128 ピクセル(推奨)
    - PNG形式
    - 128KB以下
"""
  
import argparse
import sys
from pathlib import Path
  
try:
    from PIL import Image, ImageDraw, ImageFont
except ImportError:
    print("Error: Pillow が必要です。 pip install Pillow", file=sys.stderr)
    sys.exit(1)
  
# Slack絵文字の推奨サイズ
EMOJI_SIZE = 128
MAX_FILE_SIZE = 128 * 1024  # 128KB
  
  
def find_font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
    """利用可能なフォントを探して返す。"""
    # よくあるフォントパスを試す
    font_candidates = [
        # Linux (Noto CJK — 日本語対応)
        "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc",
        "/usr/share/fonts/noto-cjk/NotoSansCJK-Bold.ttc",
        "/usr/share/fonts/truetype/noto/NotoSansCJK-Bold.ttc",
        # Linux (Droid Sans Fallback — 日本語対応)
        "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",
        # Linux (DejaVu)
        "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
        # macOS
        "/System/Library/Fonts/Hiragino Sans GB.ttc",
        "/System/Library/Fonts/Supplemental/Arial Bold.ttf",
        # Windows
        "C:/Windows/Fonts/msgothic.ttc",
        "C:/Windows/Fonts/arialbd.ttf",
    ]
    for path in font_candidates:
        if Path(path).exists():
            try:
                return ImageFont.truetype(path, size)
            except Exception:
                continue
    # フォールバック: デフォルトフォント
    return ImageFont.load_default()
  
  
def calc_font_size(text: str) -> int:
    """テキストの長さに応じたフォントサイズを返す。"""
    length = len(text)
    if length <= 1:
        return 80
    elif length <= 2:
        return 60
    elif length <= 4:
        return 40
    elif length <= 6:
        return 28
    else:
        return 22
  
  
def generate_emoji(
    text: str,
    bg_color: str = "#4A90D9",
    fg_color: str = "white",
    shape: str = "rounded",
    output: str = "emoji.png",
) -> str:
    """絵文字画像を生成してファイルパスを返す。"""
  
    img = Image.new("RGBA", (EMOJI_SIZE, EMOJI_SIZE), (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)
  
    # 背景を描画
    if shape == "circle":
        draw.ellipse([0, 0, EMOJI_SIZE - 1, EMOJI_SIZE - 1], fill=bg_color)
    else:  # rounded
        radius = 20
        draw.rounded_rectangle(
            [0, 0, EMOJI_SIZE - 1, EMOJI_SIZE - 1],
            radius=radius,
            fill=bg_color,
        )
  
    # テキストを描画
    font_size = calc_font_size(text)
    font = find_font(font_size)
  
    # テキストのバウンディングボックスで中央配置
    bbox = draw.textbbox((0, 0), text, font=font)
    text_w = bbox[2] - bbox[0]
    text_h = bbox[3] - bbox[1]
    x = (EMOJI_SIZE - text_w) / 2 - bbox[0]
    y = (EMOJI_SIZE - text_h) / 2 - bbox[1]
    draw.text((x, y), text, fill=fg_color, font=font)
  
    # 保存
    output_path = Path(output)
    img.save(str(output_path), "PNG", optimize=True)
  
    # ファイルサイズチェック
    file_size = output_path.stat().st_size
    if file_size > MAX_FILE_SIZE:
        print(
            f"Warning: ファイルサイズが {file_size:,} bytes です(上限: {MAX_FILE_SIZE:,} bytes)",
            file=sys.stderr,
        )
  
    print(f"Generated: {output_path} ({file_size:,} bytes, {EMOJI_SIZE}x{EMOJI_SIZE}px)")
    return str(output_path)
  
  
def main():
    parser = argparse.ArgumentParser(description="Slackカスタム絵文字を生成する")
    parser.add_argument("--text", required=True, help="絵文字に表示するテキスト")
    parser.add_argument("--bg", default="#4A90D9", help="背景色 (デフォルト: #4A90D9)")
    parser.add_argument("--fg", default="white", help="文字色 (デフォルト: white)")
    parser.add_argument(
        "--shape",
        choices=["circle", "rounded"],
        default="rounded",
        help="形状 (デフォルト: rounded)",
    )
    parser.add_argument("--output", default="emoji.png", help="出力ファイル名")
    args = parser.parse_args()

    generate_emoji(
        text=args.text,
        bg_color=args.bg,
        fg_color=args.fg,
        shape=args.shape,
        output=args.output,
    )
  
  
if __name__ == "__main__":
    main()

references/slack-emoji-spec.md には、Slackカスタム絵文字の仕様やデザインのベストプラクティスをまとめています。

slack-emoji-spec.mdを表示する
# Slack カスタム絵文字 仕様リファレンス
  
## アップロード要件
  
| 項目 | 仕様 |
|------|------|
| 画像形式 | PNG, GIF, JPEG |
| 推奨サイズ | 128×128 ピクセル |
| 最大ファイルサイズ | 128 KB |
| 正方形 | 必須(非正方形は自動リサイズされ崩れる) |
| 透過背景 | PNGで対応可能(推奨) |
  
## 命名規則
  
- 使用可能文字: 英小文字, 数字, ハイフン `-`, アンダースコア `_`
- 大文字・スペース・日本語は使用不可
- 例: `:lgtm:`, `:thank-you:`, `:ok_hand_custom:`
  
## デザインのベストプラクティス
  
### 視認性
- Slackの表示サイズは22×22px程度と非常に小さい
- 太いフォント・高コントラストの色を使う
- 文字数は4文字以内が理想的(それ以上は潰れる)
  
### 色の選び方
よく使われる背景色の例:
  
| 用途 | 色 | コード |
|------|------|--------|
| 承認・完了 | 緑 | `#4CAF50` |
| 注意・確認中 | 黄 | `#FFC107` |
| 却下・NG | 赤 | `#F44336` |
| 情報・汎用 | 青 | `#4A90D9` |
| 感謝・お祝い | 紫 | `#9C27B0` |
| ニュートラル | グレー | `#607D8B` |
  
### 人気のカスタム絵文字パターン
- ステータス系: `LGTM`, `WIP`, `Done`, `Help`
- リアクション系: ``, `天才`, `ありがとう`, `了解`
- 感情系: ``, `つらい`, `最高`

Strands Agents実装

agent_emoji.pyとして以下の内容でエージェントを定義します。

"""
Strands Agents で slack-emoji-generator スキルを使うデモ。
  
事前準備:
    pip install strands-agents strands-agents-tools Pillow
  
使い方:
    python agent_emoji.py
"""
  
from strands import Agent, AgentSkills
from strands_tools import file_read, file_write, shell
  
  
def main():
    # スキルをロード
    skill_plugin = AgentSkills(skills="./skills/slack-emoji-generator")
  
    # エージェントを作成
    agent = Agent(
        tools=[file_read, file_write, shell],
        plugins=[skill_plugin],
    )
  
    # 絵文字の生成を依頼
    agent("「LGTM」と「承認」のSlack絵文字を作ってください。")
  
  
if __name__ == "__main__":
    main()

実行例

前述のStrands Agentsのコードを agent_emoji.py として保存し、実行すると以下のようなやりとりが行われます。
エージェントへは "「LGTM」と「承認」のSlack絵文字を作ってください。" とだけ入力しており、スキルの利用は指示していませんが、エージェントがスキルを適切に選択して実行している様子がわかります。

% python demo/agent_emoji.py
Slackの絵文字を作成いたします。まず、slack-emoji-generatorスキルを読み込んでから、「LGTM」と「承認」の絵文字を作成します。
Tool #1: skills
それでは、「LGTM」と「承認」のSlack絵文字を作成します。まず、現在のディレクトリを確認してから、絵文字生成スクリプトがある場所に移動して作成します。
Tool #2: shell
Do you want to proceed with execution? [y/*] y
まず、スクリプトの場所を確認してから絵文字を生成します。
Tool #3: file_read
それでは、「LGTM」と「承認」の絵文字を作成します。
  
1. まずは「LGTM」の絵文字を作成(緑色の背景で承認を表現):
Tool #4: shell
Do you want to proceed with execution? [y/*] y
Generated: emoji_lgtm.png (2,788 bytes, 128x128px)
2. 次に「承認」の絵文字を作成(青色の背景で公式感を表現):
Tool #5: shell
Do you want to proceed with execution? [y/*] y
Generated: emoji_approval.png (4,986 bytes, 128x128px)
3. 生成された絵文字ファイルを確認してみましょう:
Tool #6: shell
Do you want to proceed with execution? [y/*] y
emoji_approval.png
emoji_lgtm.png
絵文字を作成しました!以下の2つのSlack絵文字が作成されました:
  
## 作成された絵文字
  
1. **「LGTM」絵文字** (`emoji_lgtm.png`)
   - サイズ: 128×128px
   - ファイルサイズ: 2,788バイト
   - 背景色: 緑色 (#4CAF50)
   - テキスト色: 白
   - 形状: 角丸四角
  
2. **「承認」絵文字** (`emoji_approval.png`)
   - サイズ: 128×128px
   - ファイルサイズ: 4,986バイト
   - 背景色: 青色 (#2196F3)
   - テキスト色: 白
   - 形状: 角丸四角
  
両方ともSlackの絵文字仕様(128×128px、PNG形式、128KB以下)に準拠しています。
  
## Slackへの追加方法
  
これらの絵文字をSlackに追加するには:
  
1. Slackワークスペースの管理者権限で、「ワークスペースの設定」→「絵文字をカスタマイズ」に移動
2. 「絵文字を追加」をクリック
3. 生成した画像ファイルをアップロード
4. 絵文字名を設定(例:`:lgtm:`、`:approval:`)
  
これで、コードレビューや承認プロセスで使いやすいSlack絵文字が完成しました!

この実行ログから、エージェントはまず Skill を有効化し、その後に file_read と shell を使って必要なファイル参照とスクリプト実行を行っていることが分かります。Skill に手順と補助資料を分けて置いておくことで、必要になったタイミングでだけ読み込ませられる点が確認できました。

生成されたpngファイルは以下のとおりで、今回は簡易実装のため見た目は素朴ですが、フォント選択や余白調整、背景テンプレートの追加などを行えば、さらに実用的な絵文字に改善できそうです。

おわりに

上記のように、エージェント本体のシステムプロンプトや実装で行うのではなく、スキルのSKILL.md内にワークフローや手順を記載しておくことで、エージェントが必要に応じてスキルを呼び出し、段階的に情報を取り込んで実行している様子がわかりました。

最初にご紹介したとおり以下のようにコード内で動的にスキルを生成して利用することも可能なため、エージェントの利用者の希望に応じてスキルを動的に生成して利用したり、利用者ごとの好みに合わせて既定のエージェントの動作をカスタマイズする、といったことも可能かと思います。

from strands import Skill
  
skill = Skill(
    name="code-review",
    description="Review code for best practices and bugs",
    instructions="Review the provided code. Check for...",
)
  
skill = Skill.from_content("""---
name: code-review
description: Review code for best practices and bugs
---
Review the provided code. Check for...
""")

今回の検証では、ワークフローや補助資料を SKILL.md / scripts/ / references/ に分けておくことで、Strands Agents から必要なタイミングで段階的に読み込み、実行できることを確認できました。

単発の処理であれば Tool として実装するだけでも十分ですが、複数ステップの手順や参考資料込みで再利用したい処理は、Skill として切り出すほうが整理しやすそうです。 チーム固有の開発フローや繰り返し使う運用手順のような、手順と文脈をまとめて持たせたいユースケースと相性がよいと感じました。

久保 賢二(執筆記事の一覧)

猫とAWSが好きです。