SpoonOS의 스킬 시스템을 활용하면, 복잡한 오케스트레이션 로직 없이도 강력한 AI 에이전트를 빠르게 만들 수 있습니다. 이 글에서는 첫 번째 스킬 에이전트를 만드는 것부터 실전 활용까지의 전 과정을 다룹니다.
스킬 에이전트는 "스킬(Skill)"이라는 모듈 단위로 AI의 역할과 도구를 정의하는 방식입니다. 기존에 AI 에이전트를 만들려면 프롬프트 엔지니어링, 도구 연결, 상태 관리 등을 직접 코딩해야 했지만, SpoonOS의 스킬 시스템을 사용하면 이 모든 것을 마크다운 파일 하나(SKILL.md)와 몇 줄의 Python 코드로 해결할 수 있습니다.
핵심 개념을 정리하면 이렇습니다:
| 개념 | 설명 |
|---|---|
| Skill | 에이전트에게 부여할 역할·지식·도구를 묶은 모듈 |
| SKILL.md | 스킬의 메타데이터(이름, 트리거, 스크립트)와 프롬프트를 정의하는 마크다운 파일 |
| Script | 스킬이 실제로 "실행"할 수 있는 Python 스크립트 (도구/Tool 역할) |
| Trigger | 사용자 입력에 따라 스킬을 자동 활성화하는 조건 |
아래 세 가지가 준비되어 있어야 합니다:
스킬은 디렉토리 단위로 관리됩니다. 먼저 폴더를 하나 만들어 주세요.
mkdir -p ./skills/my-assistant
./skills/my-assistant/SKILL.md 파일을 생성합니다. 이 파일이 스킬의 설정 파일이자 프롬프트 역할을 합니다.
파일은 크게 두 부분으로 나뉩니다:
---
name: my-assistant
description: A helpful research assistant
version: 1.0.0
tags:
- research
- assistant
triggers:
- type: keyword
keywords:
- research
- find
- search
priority: 80
---
# Research Assistant
You are now in **Research Mode**.
## Your Capabilities
- Search for information on any topic
- Analyze and summarize findings
- Provide citations and sources
## Guidelines
1. Always verify information from multiple perspectives
2. Cite your sources clearly
3. Present balanced, factual responses
여기서 triggers 부분을 주목하세요. 사용자가 "research", "find", "search" 같은 키워드를 입력하면 이 스킬이 자동으로 활성화됩니다. priority 값이 높을수록 다른 스킬보다 우선적으로 선택됩니다.
이제 Python 코드 몇 줄이면 스킬 에이전트가 완성됩니다.
import asyncio
from spoon_ai.agents import SpoonReactSkill
from spoon_ai.chat import ChatBot
async def main():
# 스킬 에이전트 생성
agent = SpoonReactSkill(
llm=ChatBot(llm_provider="openai", model_name="gpt-4o-mini"),
skill_paths=["./skills"], # 스킬 디렉토리 경로
auto_trigger_skills=True # 키워드 기반 자동 트리거 활성화
)
# 초기화 및 스킬 활성화
await agent.initialize()
await agent.activate_skill("my-assistant")
# 스킬 컨텍스트가 반영된 상태로 실행
response = await agent.run("Research the latest trends in AI")
print(response)
asyncio.run(main())
이게 끝입니다. activate_skill을 호출하면 SKILL.md의 마크다운 본문이 에이전트의 시스템 프롬프트에 자동으로 주입됩니다. 에이전트는 이제 "리서치 어시스턴트"의 역할과 가이드라인을 인지한 상태로 동작합니다.
지금까지 만든 스킬은 프롬프트만 제공하는 수준입니다. 여기에 스크립트를 추가하면 에이전트가 직접 외부 API를 호출하거나 데이터를 처리할 수 있는 실행 도구(Tool)를 갖게 됩니다.
mkdir -p ./skills/my-assistant/scripts
./skills/my-assistant/scripts/web_search.py 파일을 만듭니다. 스크립트는 stdin으로 입력을 받고, stdout으로 JSON을 출력하는 구조입니다.
#!/usr/bin/env python3
import sys
import json
def search(query: str) -> dict:
"""
실제 구현 시에는 Tavily, SerpAPI, Google Custom Search 등
원하는 검색 API를 연동하면 됩니다.
"""
return {
"status": "success",
"query": query,
"results": [
{"title": "Example Result", "url": "https://example.com"}
]
}
if __name__ == "__main__":
query = sys.stdin.read().strip()
result = search(query)
print(json.dumps(result, indent=2))
Frontmatter에 scripts 섹션을 추가합니다. 여기서 스크립트의 이름, 설명, 타입, 타임아웃 등을 정의합니다.
---
name: my-assistant
description: A helpful research assistant
version: 1.0.0
triggers:
- type: keyword
keywords: [research, find, search]
priority: 80
scripts:
enabled: true
working_directory: ./scripts
definitions:
- name: web_search
description: Search the web for information
type: python
file: web_search.py
timeout: 30
---
description은 LLM이 이 도구를 언제 사용할지 판단하는 근거가 되므로, 명확하고 구체적으로 작성하는 것이 중요합니다.
agent = SpoonReactSkill(
llm=ChatBot(llm_provider="openai", model_name="gpt-4o-mini"),
skill_paths=["./skills"],
scripts_enabled=True, # 스크립트 실행 활성화
auto_trigger_skills=True
)
이제 에이전트는 web_search를 호출 가능한 도구(Tool)로 인식합니다. 사용자가 "OO에 대해 조사해줘"라고 말하면, 에이전트가 스스로 판단해서 web_search 스크립트를 실행하고 결과를 활용합니다.
"Vibe Coding"은 SpoonOS에서 권장하는 빠른 개발 워크플로우입니다. 복잡한 설계 없이, 기존 스킬을 가져와서 프롬프트를 수정하고 바로 실행하는 방식입니다.
SpoonOS 팀에서 제공하는 spoon-awesome-skill 저장소에는 바로 사용 가능한 Web3 스킬들이 있습니다.
# 프로덕션 레벨의 Web3 스킬 가져오기
git clone https://github.com/XSpoonAi/spoon-awesome-skill.git
# 필요한 스킬만 복사
cp -r spoon-awesome-skill/web3-skills/defi ./skills/
복사한 ./skills/defi/SKILL.md를 열고 마크다운 본문을 수정합니다. 마크다운 본문이 곧 에이전트의 행동을 결정하므로, 이 부분을 바꾸는 것만으로도 에이전트의 성격과 능력이 달라집니다.
auto_trigger_skills=True를 설정하면, 사용자의 입력에 따라 적절한 스킬이 자동으로 활성화됩니다. 복잡한 if-else 분기 로직을 직접 짤 필요가 없습니다.
agent = SpoonReactSkill(
skill_paths=["./skills"],
scripts_enabled=True,
auto_trigger_skills=True,
max_auto_skills=3 # 동시에 최대 3개 스킬까지 자동 활성화
)
await agent.initialize()
# 에이전트가 사용자 입력을 보고 알아서 적절한 스킬을 활성화합니다
response = await agent.run("Get a quote to swap 1 ETH for USDC on Uniswap")
이것이 Vibe Coding의 핵심입니다. 완벽한 설계보다 빠른 반복에 초점을 맞추세요.
spoon-awesome-skill 저장소에서 제공하는 스킬들을 활용하면 Web3 에이전트를 빠르게 구축할 수 있습니다.
| 스킬 | 설명 | 주요 스크립트 |
|---|---|---|
| defi | DeFi 프로토콜 상호작용 | uniswap_quote.py, aave_positions.py |
| onchain-analysis | 온체인 데이터 분석 | etherscan_address.py, gas_tracker.py |
| nft | NFT 마켓 분석 | opensea_collection.py, nft_rarity.py |
| wallet | 지갑 조회/관리 | wallet_balance.py, portfolio_tracker.py |
| solana | Solana 생태계 | jupiter_quote.py, solana_balance.py |
| neo | Neo N3 생태계 | neo_balance.py, neo_transfer.py |
Web3 스킬의 스크립트들은 외부 API를 호출하므로, 필요한 API 키를 환경 변수로 설정해야 합니다.
export ETHERSCAN_API_KEY="your_key"
export RPC_URL="https://eth.llamarpc.com"
실제 프로젝트에서는 SpoonReactSkill을 상속해서 커스텀 에이전트 클래스를 만드는 것이 좋습니다.
from spoon_ai.agents import SpoonReactSkill
from spoon_ai.chat import ChatBot
class Web3Agent(SpoonReactSkill):
"""spoon-awesome-skill의 Web3 스킬을 활용하는 에이전트."""
def __init__(self, **kwargs):
# 기본 설정을 미리 잡아두면 사용할 때 편합니다
kwargs.setdefault('skill_paths', ['./skills'])
kwargs.setdefault('scripts_enabled', True)
kwargs.setdefault('auto_trigger_skills', True)
kwargs.setdefault('max_auto_skills', 3)
super().__init__(**kwargs)
async def initialize(self, __context=None):
await super().initialize(__context)
# 사용 가능한 스킬 목록 확인
print(f"Available skills: {self.list_skills()}")
# 자주 쓰는 스킬은 미리 활성화
if "defi" in self.list_skills():
await self.activate_skill("defi")
async def main():
agent = Web3Agent(
llm=ChatBot(llm_provider="openai", model_name="gpt-4o-mini")
)
await agent.initialize()
# DeFi 관련 질의
response = await agent.run("Get a quote to swap 1 ETH for USDC on Uniswap")
print(response)
이렇게 하면 에이전트가 DeFi 스킬의 프롬프트와 도구(uniswap_quote 스크립트 등)를 모두 갖춘 상태에서 사용자 요청을 처리합니다.
SpoonReactSkill은 스킬을 관리하기 위한 다양한 API를 제공합니다. 주요 기능별로 살펴보겠습니다.
# 사용 가능한 모든 스킬 목록
skills = agent.list_skills()
print(f"Available: {skills}")
# 특정 스킬의 상세 정보 확인
info = agent.skill_manager.get_skill_info("defi")
print(f"Description: {info['description']}")
print(f"Triggers: {info['triggers']}")
# 활성화 시 추가 컨텍스트를 전달할 수 있습니다
await agent.activate_skill("defi", {"chain": "ethereum"})
# 활성 상태 확인
is_active = agent.skill_manager.is_active("defi")
# 현재 활성화된 모든 스킬 목록
active = agent.skill_manager.get_active_skills()
# 더 이상 필요 없으면 비활성화
await agent.deactivate_skill("defi")
# 활성화된 모든 스킬의 도구 목록 가져오기
tools = agent.skill_manager.get_active_tools()
for tool in tools:
print(f"Tool: {tool.name} - {tool.description}")
# 활성 스킬들의 프롬프트 컨텍스트 합치기
context = agent.skill_manager.get_active_context()
에이전트를 거치지 않고 특정 스크립트를 직접 호출할 수도 있습니다. 디버깅이나 테스트 시 유용합니다.
result = await agent.skill_manager.execute_script(
"defi", # 스킬 이름
"uniswap_quote", # 스크립트 이름
input_text='{"token_in": "ETH", "token_out": "USDC", "amount": "1"}'
)
if result.success:
print(f"Output: {result.stdout}")
else:
print(f"Error: {result.error}")
stats = agent.get_skill_stats()
print(f"Active skills: {stats['active_skills']}")
print(f"Scripts enabled: {stats['scripts_enabled']}")
print(f"Available skills: {stats['available_skills']}")
하나의 스킬이 모든 것을 하려고 하면 프롬프트가 길어지고, 에이전트의 판단력이 떨어집니다. 하나의 스킬은 하나의 역할만 담당하도록 하고, 필요하면 여러 스킬을 조합하세요.
# 좋은 예: 역할별로 분리된 스킬을 조합
await agent.activate_skill("defi")
await agent.activate_skill("wallet")
# 나쁜 예: 하나의 거대한 스킬에 모든 것을 넣기
# await agent.activate_skill("everything-web3") # 이러지 마세요
여러 스킬의 트리거 키워드가 겹칠 수 있습니다. priority 값을 적절히 조절해서 충돌을 방지하세요.
triggers:
# 구체적인 키워드에는 높은 우선순위
- type: keyword
keywords: [uniswap, aave]
priority: 90
# 일반적인 키워드에는 낮은 우선순위
- type: keyword
keywords: [swap, trade]
priority: 70
예를 들어 사용자가 "uniswap에서 swap해줘"라고 말하면, "uniswap" 키워드(priority 90)가 먼저 매칭되어 해당 스킬이 활성화됩니다.
스킬 활성화가 실패할 수 있는 상황(잘못된 이름, 누락된 파일 등)에 대비하세요.
try:
await agent.activate_skill("my-skill")
except ValueError as e:
print(f"Skill activation failed: {e}")
# 대체 스킬로 폴백하거나, 사용자에게 알림
스크립트는 외부에서 입력을 받으므로, 잘못된 데이터가 들어올 수 있습니다. 방어적으로 코딩하세요.
import json
import sys
def main():
try:
input_data = json.loads(sys.stdin.read())
except json.JSONDecodeError:
print(json.dumps({"error": "Invalid JSON input"}))
sys.exit(1)
# 필수 필드 확인
if "query" not in input_data:
print(json.dumps({"error": "Missing required field: query"}))
sys.exit(1)
# 정상 처리...
모든 스킬이 스크립트를 필요로 하는 건 아닙니다. 프롬프트만 제공하는 스킬이라면 명시적으로 꺼두세요.
scripts:
enabled: false # 이 스킬은 프롬프트만 제공합니다
ValueError: Skill 'my-skill' not found
다음을 확인하세요:
skill_paths에 지정된 경로 안에 있는지SKILL.md 파일이 있는지name 필드가 activate_skill()에 전달한 이름과 일치하는지ScriptResult(success=False, error="Script timed out")
다음을 확인하세요:
timeout 값이 충분한지 (외부 API 호출이 있다면 더 길게)chmod +x)다음을 순서대로 확인하세요:
auto_trigger_skills=True가 설정되어 있는지triggers가 정의되어 있는지priority가 다른 스킬보다 충분히 높은지max_auto_skills 한도에 도달하지 않았는지SpoonOS의 스킬 시스템을 사용하면 마크다운 파일 하나로 에이전트의 역할을 정의하고, 간단한 스크립트로 실행 능력을 부여할 수 있습니다. 핵심은 다음 세 가지입니다: