背景・目的
普段からKiroを使っているが、Skills機能についてはあまり触ってきませんでした。
チームメンバーが作成したスキルを見る機会があり、これを機に概念を整理し、実際に自分でスキルを作って動かしてみます。
まとめ
| 特徴 | 説明 |
|---|---|
| Skillsとは | 構造化されたプロンプトを再利用可能な単位としてパッケージ化したもの |
| 配置場所 |
.kiro/skills/ (ワークスペース) または ~/.kiro/skills/ (グローバル) |
| 必須ファイル |
SKILL.md のみ。references/ や assets/ はオプション |
| 呼び出し方 | 自動発動(descriptionマッチ)またはスラッシュコマンド(/スキル名) |
| 本質 | チームの暗黙知をAIが実行可能な形式知にしたもの |
概要
Skillsとは
下記を基に整理する。
Skillsは、AIへの指示(プロンプト)を再利用可能な単位としてパッケージ化したものである。
Skills are portable instruction packages that extend what Kiro knows how to do. Each skill contains instructions for a specific workflow, whether that's reviewing pull requests, deploying infrastructure, or following your team's coding standards.
通常、AIに作業を依頼する際は毎回プロンプトを手書きする。Skillsを使うと、「いつ発動するか」「何を入力とするか」「どう処理するか」「どう出力するか」をファイルに定義しておき、決まったフレーズやスラッシュコマンドで呼び出せる。
下記に筆者の理解としてプロンプトとスキルの違いを整理する。
| プロンプト | スキル | |
|---|---|---|
| 定義 | AIへの指示文 | プロンプトを再利用可能な単位としてパッケージ化したもの |
| 構成 | 自由記述 | 発動条件・インプット・実行手順・出力フォーマットをセットにしたもの |
| 作成 | その場で都度書く | 事前に定義しておく |
| 利用 | 書いた本人がその場で使う | チーム内の誰でも同じフレーズで呼び出せる |
| 共有 | コピペ |
.kiro/skills/ に置いてリポジトリにコミット |
一言で言えば、「毎回プロンプトを書き直さなくていいように、チームで共有できる形にしたプロンプト」である。
なお、Agent Skills は Kiro 独自の機能ではなく、Anthropic が開発しオープンスタンダードとして公開した仕様である。同じ SKILL.md フォーマットが複数のコードエージェントで利用できる。
| エージェント | 提供元 | Skills対応 |
|---|---|---|
| Kiro | AWS | ✅ |
| Claude Code | Anthropic | ✅(発祥) |
| Codex | OpenAI | ✅ |
| VS Code (Copilot) | Microsoft/GitHub | ✅ |
スキルの構成
下記を基に整理する。
スキルは SKILL.md を含むディレクトリである。
skill-name/
├── SKILL.md # 必須:メタデータ + 手順
├── scripts/ # 任意:実行可能コード
├── references/ # 任意:追加ドキュメント
├── assets/ # 任意:テンプレート等
| ディレクトリ | 役割 |
|---|---|
SKILL.md |
スキルの定義本体。frontmatter(メタデータ)+ Markdownの手順 |
scripts/ |
エージェントが実行できるコード。Python, Bash, JavaScript等 |
references/ |
エージェントが必要に応じて読む追加ドキュメント |
assets/ |
テンプレート、画像、データファイル等の静的リソース |
Progressive Disclosure(段階的読み込み)
Agent Skills の設計の核心は、コンテキストウィンドウを節約しつつ多数のスキルを登録できる仕組みにある。
-
Discovery(起動時):
nameとdescriptionだけ読む(~100トークン) - Activation(発動時): SKILL.md 本文を全て読む(5000トークン以下推奨)
- Execution(実行時): scripts/ references/ assets/ を必要に応じて読む
このため、SKILL.md は 500行以下に抑え、詳細なリファレンスは references/ に分離することが推奨されている。
SKILL.md のフォーマット
SKILL.md は YAML frontmatter + Markdown 本文で構成される。
Frontmatter
| フィールド | 必須 | 制約 |
|---|---|---|
name |
✅ | 64文字以内。小文字英数字+ハイフンのみ。ハイフン始まり/終わり不可。親ディレクトリ名と一致する必要あり |
description |
✅ | 1024文字以内。何をするか+いつ使うかを記述。エージェントがタスクマッチに使う |
license |
- | ライセンス名またはライセンスファイルへの参照 |
compatibility |
- | 500文字以内。環境要件(必要なツール、ネットワークアクセス等) |
metadata |
- | 任意のキーバリュー(author, version等) |
allowed-tools |
- | スペース区切りの事前承認ツール一覧(実験的) |
最小構成の例
---
name: run-elt-test
description: ELTの単体テストを実行する。テスト実行を依頼されたときに使う。
---
オプションフィールドを含む例
---
name: pdf-processing
description: Extract PDF text, fill forms, merge files. Use when handling PDFs.
license: Apache-2.0
compatibility: Requires Python 3.14+ and uv
metadata:
author: example-org
version: "1.0"
---
本文(Body)
frontmatter の後に Markdown で手順を記述する。フォーマットに制約はなく、エージェントがタスクを実行するのに役立つ内容を書く。推奨セクション:
- ステップバイステップの手順
- 入出力の例
- よくあるエッジケース
ファイル参照
スキル内の他ファイルを参照する場合は、スキルルートからの相対パスを使う。
詳細は [リファレンスガイド](references/REFERENCE.md) を参照。
抽出スクリプトを実行:
scripts/extract.py
配置と呼び出し
下記を基に整理する。
配置場所
| 場所 | スコープ | 用途 |
|---|---|---|
.kiro/skills/ |
ワークスペース | プロジェクト固有のワークフロー、チーム規約 |
~/.kiro/skills/ |
グローバル | 個人用ワークフロー(全プロジェクト共通) |
同名のスキルがある場合、ワークスペース側が優先される。
呼び出し方
スキルは2通りの方法で呼び出せる。
1. 自動発動
Kiroがユーザーのリクエストとスキルの description を照合し、マッチしたスキルを自動でロードする。
> ELTテスト実行して
I'll run the ELT test using the test script...
2. スラッシュコマンド
/スキル名 で明示的に呼び出す。name フィールドがそのままコマンド名になる。
> /run-elt-test rep001 tc1
I'll run the ELT test for rep001/tc1...
スラッシュコマンドの後に続けたテキストは、スキルへの引数として渡される。
確認方法
現在ロードされているスキル一覧は /context show で確認できる。
> /context show
使い方のパターン
| パターン | 例 | 複雑さ |
|---|---|---|
| スクリプト実行 | 「テスト実行して」→ シェルスクリプト実行 | ★☆☆ |
| コード生成 | 「CRUDのAPI作って」→ テンプレートに沿って生成 | ★★☆ |
| チェックリスト | 「レビュー前チェック」→ 観点に沿って検査・指摘 | ★★☆ |
| ワークフロー | 「PRレビューして」→ 情報収集→判断→レポート出力 | ★★★ |
| ドキュメント生成 | 「設計書書いて」→ 規約に沿った文書作成 | ★★☆ |
| ナレッジ参照 | 「このエラーの対処法は?」→ 蓄積知識から回答 | ★☆☆ |
実践
ELTの単体テストを実行するシェルスクリプトをスキル化し、Kiroから /run-elt-test で呼び出せるようにする。
スキルの作成から実行までの一連の流れを確認することが目的である。
Redshift Serverless を構築し、Redshift Data API 経由で実際に SQL 実行・結果比較まで行う。
前提
- Kiro CLI がインストール済み
- 作業ディレクトリにプロジェクトが存在する
今回下記のような構成で検証する。
プロジェクト/
├── .kiro/
│ └── skills/
│ └── run-elt-test/
│ ├── SKILL.md
│ └── scripts/
│ └── run_elt_test.sh
├── ddl/
│ ├── sales.sql
│ └── daily_sales_summary.sql
├── sql/
│ └── daily_sales_summary.sql
└── testdata/
└── rep001/
└── tc1/
├── input.csv
└── expected.csv
Redshift Serverlessの構築
AWSにRedshift Serverlessを構築します
1. cdk-stack.tsを用意します
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as redshiftserverless from 'aws-cdk-lib/aws-redshiftserverless';
export class CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const namespace = new redshiftserverless.CfnNamespace(this, 'Namespace', {
namespaceName: 'skills-lab-ns',
dbName: 'skillsdb',
adminUsername: 'admin',
manageAdminPassword: true,
});
const workgroup = new redshiftserverless.CfnWorkgroup(this, 'Workgroup', {
workgroupName: 'skills-lab-wg',
namespaceName: namespace.attrNamespaceNamespaceName,
baseCapacity: 4,
publiclyAccessible: false,
});
workgroup.addDependency(namespace);
}
}
2. cdk.tsを用意します
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib/core';
import { Aspects } from 'aws-cdk-lib/core';
import { AwsSolutionsChecks } from 'cdk-nag';
import { CdkStack } from '../lib/cdk-stack';
const app = new cdk.App();
new CdkStack(app, 'SkillsLabRedshiftStack', {
env: { region: 'ap-northeast-1' },
});
Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
3. テストクラス用意します
import * as cdk from 'aws-cdk-lib/core';
import { Template } from 'aws-cdk-lib/assertions';
import { CdkStack } from '../lib/cdk-stack';
describe('SkillsLabRedshiftStack', () => {
const app = new cdk.App();
const stack = new CdkStack(app, 'TestStack');
const template = Template.fromStack(stack);
test('Namespace が作成される', () => {
template.hasResourceProperties('AWS::RedshiftServerless::Namespace', {
NamespaceName: 'skills-lab-ns',
DbName: 'skillsdb',
AdminUsername: 'admin',
ManageAdminPassword: true,
});
});
test('Workgroup が作成される', () => {
template.hasResourceProperties('AWS::RedshiftServerless::Workgroup', {
WorkgroupName: 'skills-lab-wg',
BaseCapacity: 4,
PubliclyAccessible: false,
});
});
test('Workgroup は Namespace に依存する', () => {
template.hasResource('AWS::RedshiftServerless::Workgroup', {
DependsOn: ['Namespace'],
});
});
});
4. テストをします
npx jest
PASS test/cdk.test.ts (7.84 s)
SkillsLabRedshiftStack
✓ Namespace が作成される (7 ms)
✓ Workgroup が作成される
✓ Workgroup は Namespace に依存する
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 8.004 s
Ran all test suites.
- Completed in 9.495s
> 全テスト通りました。
✓ Namespace が作成される
✓ Workgroup が作成される
✓ Workgroup は Namespace に依存する
5. デプロイします
npx cdk deploy --require-approval never
DDLの作成
下記のようなDDLを配置します
ddl/
├── sales.sql ← ソーステーブル
└── daily_sales_summary.sql ← ELT結果テーブル
1. ソーステーブルを作成します
CREATE TABLE IF NOT EXISTS public.sales (
sale_date DATE,
product VARCHAR(50),
quantity INT,
unit_price INT
);
2. ELT結果テーブルを作成します
CREATE TABLE IF NOT EXISTS public.daily_sales_summary (
sale_date DATE,
product VARCHAR(50),
total_quantity INT,
total_amount INT
);
3. DDLを作成します
SQL=$(cat ddl/sales.sql ddl/daily_sales_summary.sql)
aws redshift-data execute-statement \
--workgroup-name skills-lab-wg \
--database skillsdb \
--sql "$SQL" \
--region ap-northeast-1 \
--output text --query 'Id'
4. 確認します。成功しました
✓ Trusted: AWS_PROFILE=XXXXXXX aws redshift-data describe-statement \
--id XXXXXXX \
--region ap-northeast-1 --query '[Status, Error]' --output json
[
"FINISHED",
null
]
DMLの作成
1. sql/ の下に集計用のクエリを用意します
INSERT INTO public.daily_sales_summary
SELECT
sale_date,
product,
SUM(quantity) AS total_quantity,
SUM(quantity * unit_price) AS total_amount
FROM public.sales
GROUP BY sale_date, product;
スキルの作成
SKILL.md
1. 下記を用意します。
---
name: run-elt-test
description: ELTの単体テストを実行する。テスト実行、ELTテスト、単体テストを依頼されたときに使う。
---
# ELT単体テスト実行
## 手順
1. ユーザーから REPORT_ID と テストケース名(TC)を受け取る
2. `scripts/run_elt_test.sh <REPORT_ID> <TC>` を実行する
3. 結果を確認し、成功/失敗を報告する
## 使い方
/run-elt-test rep001 tc1
## 補足
- テストデータは `testdata/{REPORT_ID}/{TC}/` に配置する
- 期待値は `expected.csv`、実績は `actual.csv` として出力される
- 本番環境では Redshift Serverless に接続して SQL を実行する
scripts
1. .kiro/skills/run-elt-test/scripts/run_elt_test.sh を作成する。
.kiro/skills/run-elt-test/scripts/run_elt_test.sh を作成する。Redshift Data API を使い、テストデータのロード → ELT SQL実行 → 結果比較を行う
#!/bin/bash
# ELT単体テスト実行スクリプト(Redshift Data API版)
# Usage: ./run_elt_test.sh <REPORT_ID> <TC>
# 例: ./run_elt_test.sh rep001 tc1
#
# 環境変数:
# AWS_PROFILE: AWSプロファイル(必須)
# WORKGROUP: Redshift Serverless ワークグループ(デフォルト: skills-lab-wg)
# DATABASE: データベース名(デフォルト: skillsdb)
# REGION: AWSリージョン(デフォルト: ap-northeast-1)
set -euo pipefail
REPORT_ID="${1:?Usage: $0 <REPORT_ID> <TC> (例: rep001 tc1)}"
TC="${2:?Usage: $0 <REPORT_ID> <TC> (例: rep001 tc1)}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
TESTDATA_DIR="$PROJECT_ROOT/testdata"
SQL_DIR="$PROJECT_ROOT/sql"
TC_DIR="$TESTDATA_DIR/${REPORT_ID}/${TC}"
WORKGROUP="${WORKGROUP:-skills-lab-wg}"
DATABASE="${DATABASE:-skillsdb}"
REGION="${REGION:-ap-northeast-1}"
PROFILE="${AWS_PROFILE:?環境変数 AWS_PROFILE を設定してください}"
echo "=========================================="
echo "ELT単体テスト: ${REPORT_ID} / ${TC}"
echo "WORKGROUP: $WORKGROUP"
echo "DATABASE: $DATABASE"
echo "=========================================="
# 存在チェック
[[ -d "$TC_DIR" ]] || { echo "❌ テストケースが見つかりません: $TC_DIR"; exit 1; }
[[ -f "$TC_DIR/expected.csv" ]] || { echo "❌ expected.csv が見つかりません"; exit 1; }
# SQL実行関数(ポーリング付き)
exec_sql() {
local label="$1"
local sql="$2"
echo ""
echo "--- $label ---"
local RESULT
RESULT=$(aws redshift-data execute-statement \
--workgroup-name "$WORKGROUP" \
--database "$DATABASE" \
--sql "$sql" \
--profile "$PROFILE" \
--region "$REGION" \
--output json)
local STMT_ID
STMT_ID=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['Id'])")
while true; do
local STATE
STATE=$(aws redshift-data describe-statement --id "$STMT_ID" \
--profile "$PROFILE" --region "$REGION" --query 'Status' --output text)
if [[ "$STATE" == "FINISHED" ]]; then
echo " ✅ $STATE"
echo "$STMT_ID"
return 0
elif [[ "$STATE" == "FAILED" ]]; then
local ERR
ERR=$(aws redshift-data describe-statement --id "$STMT_ID" \
--profile "$PROFILE" --region "$REGION" --query 'Error' --output text)
echo " ❌ $STATE: $ERR"
return 1
fi
sleep 2
done
}
# Step 1: TRUNCATE
echo ""
echo "===== Step 1: 環境クリア ====="
exec_sql "TRUNCATE sales" "TRUNCATE TABLE public.sales;" > /dev/null
exec_sql "TRUNCATE daily_sales_summary" "TRUNCATE TABLE public.daily_sales_summary;" > /dev/null
# Step 2: テストデータロード
echo ""
echo "===== Step 2: テストデータロード ====="
INPUT_CSV="$TC_DIR/input.csv"
[[ -f "$INPUT_CSV" ]] || { echo "❌ input.csv が見つかりません"; exit 1; }
INSERT_SQL=$(python3 -c "
import csv
with open('$INPUT_CSV', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
cols = reader.fieldnames
rows = list(reader)
if not rows:
exit(0)
col_list = ', '.join(cols)
values_list = []
for row in rows:
vals = []
for c in cols:
v = row[c]
if v == '\\\\N':
vals.append('NULL')
else:
vals.append(\"'\" + v.replace(\"'\", \"''\") + \"'\")
values_list.append('(' + ', '.join(vals) + ')')
print(f'INSERT INTO public.sales ({col_list}) VALUES ' + ', '.join(values_list) + ';')
")
exec_sql "INSERT sales" "$INSERT_SQL" > /dev/null
ROW_COUNT=$(python3 -c "
import csv
with open('$INPUT_CSV', 'r', encoding='utf-8') as f:
print(sum(1 for _ in csv.DictReader(f)))
")
echo " sales: ${ROW_COUNT} rows loaded"
# Step 3: ELT SQL実行
echo ""
echo "===== Step 3: ELT SQL実行 ====="
ELT_SQL=$(cat "$SQL_DIR/daily_sales_summary.sql")
exec_sql "daily_sales_summary.sql" "$ELT_SQL" > /dev/null
# Step 4: 結果比較
echo ""
echo "===== Step 4: 結果比較 ====="
EXPECTED_CSV="$TC_DIR/expected.csv"
COLUMNS=$(head -1 "$EXPECTED_CSV")
SELECT_SQL="SELECT ${COLUMNS} FROM public.daily_sales_summary ORDER BY 1,2;"
RESULT=$(aws redshift-data execute-statement \
--workgroup-name "$WORKGROUP" \
--database "$DATABASE" \
--sql "$SELECT_SQL" \
--profile "$PROFILE" \
--region "$REGION" \
--output json)
STMT_ID=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['Id'])")
while true; do
STATE=$(aws redshift-data describe-statement --id "$STMT_ID" \
--profile "$PROFILE" --region "$REGION" --query 'Status' --output text)
if [[ "$STATE" == "FINISHED" ]]; then break; fi
if [[ "$STATE" == "FAILED" ]]; then echo " ❌ SELECT failed"; exit 1; fi
sleep 2
done
ACTUAL_CSV="$TC_DIR/actual.csv"
aws redshift-data get-statement-result --id "$STMT_ID" \
--profile "$PROFILE" --region "$REGION" --output json \
| python3 -c "
import csv, sys, json
data = json.load(sys.stdin)
columns = [c['name'] for c in data['ColumnMetadata']]
rows = []
for record in data['Records']:
row = {}
for i, col in enumerate(columns):
cell = record[i]
if 'isNull' in cell and cell['isNull']:
val = ''
elif 'stringValue' in cell:
val = cell['stringValue']
elif 'longValue' in cell:
val = str(cell['longValue'])
elif 'doubleValue' in cell:
val = str(cell['doubleValue'])
else:
val = ''
row[col] = val.strip()
rows.append(row)
with open('$ACTUAL_CSV', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=columns, lineterminator='\n')
writer.writeheader()
writer.writerows(rows)
print(f' 実績: {len(rows)}件')
"
# 比較
python3 -c "
import csv, sys
def load_csv(path):
with open(path, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
fields = [c.strip() for c in reader.fieldnames]
rows = sorted([{k.strip(): v.strip() for k, v in r.items()} for r in reader], key=lambda r: tuple(r.values()))
return rows, fields
expected, fields = load_csv('$EXPECTED_CSV')
actual, _ = load_csv('$ACTUAL_CSV')
print(f' 期待件数: {len(expected)}')
print(f' 実績件数: {len(actual)}')
if len(expected) != len(actual):
print(' ❌ 件数不一致')
sys.exit(1)
errors = []
for i, (e, a) in enumerate(zip(expected, actual)):
for col in fields:
ev = str(e.get(col, '')).strip()
av = str(a.get(col, '')).strip()
if ev != av:
errors.append(f' 行{i+1} [{col}]: 期待=\"{ev}\" 実績=\"{av}\"')
if errors:
print(f' ❌ {len(errors)}件の差分:')
for err in errors:
print(err)
sys.exit(1)
else:
print(f' ✅ 全{len(expected)}行×{len(fields)}列 一致(テスト成功)')
"
テストデータの準備
testdata/rep001/tc1/ に配置する。
input.csv(salesテーブルへの投入データ)
sale_date,product,quantity,unit_price
2026-01-01,商品A,3,1000
2026-01-01,商品A,2,1000
2026-01-01,商品B,1,2000
2026-01-02,商品A,5,1000
2026-01-02,商品B,3,2000
expected.csv(期待値)
sale_date,product,total_quantity,total_amount
2026-01-01,商品A,5,5000
2026-01-01,商品B,1,2000
2026-01-02,商品A,5,5000
2026-01-02,商品B,3,6000
実行
テストケース1
- 下記を実行します
7% > run-elt-test rep001 tc1
Running tool SkillsTool with the param (from mcp server: builder-mcp)
⋮ {
⋮ "skillName": "run-elt-test"
⋮ }
Allow this action? Use 't' to trust (always allow) the 'SkillsTool' tool for the session. [y/n/t]:
9% > t
- Completed in 0.471s
> The skill tool doesn't have run-elt-test registered, but there's a skill file at the path indicated. Let me read it
directly.
Reading file: /XXXXX/XXXXX/XXXXX/XXXXX/XXXXX/skills/.kiro/skills/run-elt-test/SKILL.md, all lines (using tool: read)
✓ Successfully read 907 bytes from /XXXXX/XXXXX/XXXXX/XXXXX/XXXXX/skills/.kiro/skills/run-elt-test/SKILL.md
- Completed in 0.1s
> スキルの手順に従い、テストスクリプトを実行します。
I will run the following command: .kiro/skills/run-elt-test/scripts/run_elt_test.sh rep001 tc1 (using tool: shell)
Purpose: ELT単体テスト rep001 tc1 を実行
Allow this action? Use 't' to trust (always allow) the 'shell' tool for the session. [y/n/t]:
9% > t
✓ Trusted: .kiro/skills/run-elt-test/scripts/run_elt_test.sh rep001 tc1
==========================================
ELT単体テスト: rep001 / tc1
WORKGROUP: skills-lab-wg
DATABASE: skillsdb
==========================================
===== Step 1: 環境クリア =====
===== Step 2: テストデータロード =====
sales: 5 rows loaded
===== Step 3: ELT SQL実行 =====
===== Step 4: 結果比較 =====
実績: 4件
期待件数: 4
実績件数: 4
✅ 全4行×4列 一致(テスト成功)
- Completed in 47.856s
> テスト成功 ✅
- **REPORT_ID**: rep001
- **テストケース**: tc1
- テストデータ(sales): 5行ロード
- 実績: 4件、期待値と全4行×4列が一致
テストはすべてパスしました。
▸ Time: 1m 12s
9% >
2. 念のため2つのファイルを確認します
% ls -l
total 24
-rw-r--r--@ 1 XXXXX XXXXX 150 5 30 21:40 actual.csv
-rw-r--r--@ 1 XXXXX XXXXX 150 5 30 20:52 expected.csv
-rw-r--r--@ 1 XXXXX XXXXX 168 5 30 20:52 input.csv
% diff expected.csv actual.csv
%
テストケース2
1. 失敗するケースを実行してみます。
run-elt-test rep001 tc2
テストを実行して確認します。
I will run the following command: .kiro/skills/run-elt-test/scripts/run_elt_test.sh rep001 tc2 2>&1 || true (using tool: shell)
Purpose: ELT単体テスト rep001 tc2 を実行(エラー出力も取得)
Allow this action? Use 't' to trust (always allow) the 'shell' tool for the session. [y/n/t]:
10% > t
✓ Trusted: true
==========================================
ELT単体テスト: rep001 / tc2
WORKGROUP: skills-lab-wg
DATABASE: skillsdb
==========================================
===== Step 1: 環境クリア =====
===== Step 2: テストデータロード =====
sales: 2 rows loaded
===== Step 3: ELT SQL実行 =====
===== Step 4: 結果比較 =====
実績: 2件
期待件数: 2
実績件数: 2
❌ 1件の差分:
行2 [total_amount]: 期待="9999" 実績="2000"
- Completed in 15.942s
> テスト失敗 ❌
今回はテスト自体は最後まで実行されましたが、結果比較で差分が検出されました。
結果:
- 期待件数: 2件 / 実績件数: 2件(件数は一致)
- **差分**: 行2 の total_amount — 期待値 9999、実績 2000
ELT SQL の集計ロジックか、テストデータ(input.csv / expected.csv)の期待値に問題がある可能性があります。テストデータと SQL
を確認しますか?
▸ Time: 28s
10% >
2. ファイルを比較します
% ls -ltr
total 24
-rw-r--r--@ 1 XXXXXXX XXXXXXX 90 5 30 21:47 input.csv
-rw-r--r--@ 1 XXXXXXX XXXXXXX 98 5 30 21:47 expected.csv
-rw-r--r--@ 1 XXXXXXX XXXXXXX 98 5 30 21:47 actual.csv
% diff expected.csv actual.csv
3c3
< 2026-01-01,商品B,1,9999
---
> 2026-01-01,商品B,1,2000
%
考察
Skills は「チームの暗黙知をAIが実行可能な形式知にする」仕組みである。スクリプト自体は従来通り書けばよく、SKILL.md はそのスクリプトへの「入口」を定義するだけ。導入コストは低い。
ただし、Kiroは指示を「文字通り」ではなく「意図」で解釈するため、確実にスクリプトを実行させたい場合は強制的な表現が必要になる。この点はプロンプトエンジニアリングと同じ感覚で調整する。
スキル化して何が嬉しかったか
| 観点 | スキル化前 | スキル化後 |
|---|---|---|
| 実行方法 | スクリプトのパスを覚えて手動実行 | /run-elt-test rep001 tc1 で完結 |
| 引数の説明 | READMEを読む or スクリプト冒頭のコメントを見る | SKILL.md に使い方が書いてあり、Kiroが案内してくれる |
| 結果の解釈 | ターミナル出力を自分で読む | Kiroが成功/失敗を判定し、差分があれば原因を示唆してくれる |
| チーム共有 | 「このスクリプトこう使って」と口頭で伝える | リポジトリにコミットすれば誰でも同じ体験 |
一番の価値は「スクリプトの存在を知らなくても、自然言語で呼び出せる」こと。新メンバーが「ELTテスト実行して」と言えば動く。
どういう業務に向いているか
- 繰り返し実行する定型作業(テスト実行、デプロイ、データ投入)
- 手順が決まっているが引数が毎回変わる作業(レポートID × テストケースの組み合わせ)
- 結果の判定ロジックがある作業(diff、閾値チェック、ステータス確認)
逆に向かないのは、毎回やることが違う探索的な作業や、人間の判断が多く入る作業。
ハマりポイント
| 問題 | 原因 | 対策 |
|---|---|---|
| No such file or directory | SKILL.md 内のパスがスキルルート相対だが、Kiroはプロジェクトルートから実行する | プロジェクトルートからのフルパスで記述する |
| スクリプトが実行されず diff だけで済まされる | Kiroは「目的達成」を優先し、既存の actual.csv があるとスクリプトをスキップする | 「必ずスクリプトを実行すること」と明記 + actual.csv を削除 |
| SKILL.md の変更が反映されない | セッション中はキャッシュされる | セッションを再起動する |
| 環境変数未設定でスクリプトが失敗 | set -euo pipefail + ${AWS_PROFILE:?...} |
SKILL.md の前提条件に環境変数を明記する |
参考


