Skills
Skills (스킬)
에이전트 스킬(Agent Skills)은 에이전트에게 작업을 위한 재사용 가능한 지시와 지원 파일을 제공해요. Responses API 셸 도구와 함께 사용하거나 Agents API 샌드박스에서 사용 가능하게 만들 수 있어요.
아래의 업로드·연결·버전 관리 지시는 Responses API 셸 도구를 설명해요. Agents API 세션은 샌드박스의 디렉터리에서 스킬을 발견해요.
Responses API는 두 가지 폼 팩터로 스킬을 지원해요: 로컬 실행과 호스티드 컨테이너 기반 실행. 자신의 머신에서 코드를 실행하려면 셸 도구의 로컬 실행 모드를 사용하세요.
출처: 문서
본문
스킬이란 무엇인가
스킬은 SKILL.md 매니페스트(프론트매터 + 지시)가 있는 파일 디렉터리예요. 스킬은 회사 스타일 가이드부터 다단계 워크플로까지 프로세스와 관례를 코드로 만들 수 있는 모듈식 지시예요. 업로드한 스킬은 버전 관리되는 번들을 사용해요.
스킬은 공개 Agent Skills 표준과 호환돼요.
예시 SKILL.md
---
name: basic-math
description: Add or multiply numbers.
---
Use this skill when you need a quick sum or product of numbers.
스킬 발견(discovery) 동안 모델은 스킬의 이름과 설명을 봐요. 스킬이 무엇을 하는지와 언제 사용하는지 둘 다 설명하는 설명을 작성하세요. 예를 들어 "폴백 조항을 사용해 벤더 계약을 검토하고 수정 제안(redline)해라"가 "법률 작업에 도움"보다 모델에 더 유용한 맥락을 줘요.
주요 지시는 SKILL.md에 두고 필요에 따라 지원 파일을 연결하세요.
review-pr/
├── SKILL.md
├── references/
│ └── review-guidelines.md
├── scripts/
│ └── check-changes.sh
└── assets/
└── review-template.md
배경 자료는 references/, 반복 가능한 액션은 scripts/, 재사용 가능한 템플릿은 assets/에 두세요.
스킬 생성
디렉터리를 multipart form data로 업로드하거나, 단일 최상위 폴더를 담은 .zip을 업로드할 수 있어요.
옵션 1: 디렉터리 업로드 (multipart)
여러 files[] 파트를 업로드하세요. 각 파트는 단일 최상위 폴더 안의 경로를 포함해요.
스킬 생성 (multipart)
curl -X POST 'https://api.openai.com/v1/skills' \
-H "Authorization: Bearer ***" \
-F 'files[]=@./basic_math/SKILL.md;filename=basic_math/SKILL.md;type=text/markdown' \
-F 'files[]=@./basic_math/calculate.py;filename=basic_math/calculate.py;type=text/plain'
옵션 2: Zip 업로드
최상위 폴더를 압축하고 zip 파일을 업로드하세요.
스킬 생성 (zip)
curl -X POST 'https://api.openai.com/v1/skills' \
-H "Authorization: Bearer ***" \
-F 'files=@./basic_math.zip;type=application/zip'
호스티드 셸과 함께 스킬 사용
호스티드 셸 환경에 스킬을 마운트하려면 셸 도구를 호출할 때 tools[].environment.skills로 연결하세요.
호스티드 셸에서 스킬 사용
curl -L 'https://api.openai.com/v1/responses' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"tools": [
{
"type": "shell",
"environment": {
"type": "container_auto",
"skills": [
{ "type": "skill_reference", "skill_id": "<skill_id>" },
{ "type": "skill_reference", "skill_id": "<skill_id>", "version": 2 }
]
}
}
],
"input": "Use the skills to add 144 and 377, then compute triangle area with base 9 height 13."
}'
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
tools: [
{
type: "shell",
environment: {
type: "container_auto",
skills: [
{ type: "skill_reference", skill_id: "<skill_id>" },
{ type: "skill_reference", skill_id: "<skill_id>", version: "2" },
],
},
},
],
input:
"Use the skills to add 144 and 377, then compute triangle area with base 9 height 13.",
});
console.log(response.output_text);
response = client.responses.create(
model="gpt-6-astra",
tools=[
{
"type": "shell",
"environment": {
"type": "container_auto",
"skills": [
{"type": "skill_reference", "skill_id": "<skill_id>"},
{
"type": "skill_reference",
"skill_id": "<skill_id>",
"version": 2,
},
],
},
}
],
input="Use the skills to add 144 and 377, then compute triangle area with base 9 height 13.",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerAuto: &responses.ContainerAutoParam{
Skills: []responses.ContainerAutoSkillUnionParam{
{OfSkillReference: &responses.SkillReferenceParam{SkillID: "<skill_id>"}},
{OfSkillReference: &responses.SkillReferenceParam{SkillID: "<skill_id>", Version: openai.String("2")}},
},
}},
}}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Use the skills to add 144 and 377, then compute triangle area with base 9 height 13.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.ResponseCreateParams;
import java.util.List;
import java.util.Map;
String skillId = "<skill_id>";
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input(
"Use the skills to add 144 and 377, then compute a triangle area with base 9 and height 13.")
.putAdditionalBodyProperty(
"tools",
JsonValue.from(
List.of(
Map.of(
"type",
"shell",
"environment",
Map.of(
"type",
"container_auto",
"skills",
List.of(
Map.of("type", "skill_reference", "skill_id", skillId),
Map.of(
"type", "skill_reference",
"skill_id", skillId,
"version", "2")))))))
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Use the skills to add 144 and 377, then compute a triangle area with base 9 and height 13.",
tools: [
{
type: :shell,
environment: {
type: :container_auto,
skills: [
{
type: :skill_reference,
skill_id: "<skill_id>"
},
{
type: :skill_reference,
skill_id: "<skill_id>",
version: "2"
}
]
}
}
]
)
puts(response.output_text)
프롬프팅 동작
스킬이 마운트되면 모델이 언제 스킬을 사용할지 결정할 수 있어요. 더 결정적인 동작을 원하면 모델에게 적절할 때 "<skill name> 스킬을 사용해"라고 명시적으로 지시하세요.
로컬 셸 모드와 함께 스킬 사용
스킬은 로컬 셸 모드에서도 동작하지만, 로컬 셸과 호스티드 셸은 같은 스킬 연결 형식을 받지 않아요.
- 호스티드 셸은 업로드된
skill_reference연결을 지원하며, 큐레이티드 스킬과 명시적 버전을 포함해요. - 로컬 셸은
skill_reference연결을 지원하지 않아요. 대신 여러분이 통제하는 런타임의 로컬 파일 경로에서 스킬 파일을 제공하세요.
로컬 셸 실행 세부 사항은 Shell 가이드를 사용하세요.
로컬 셸 모드에서 스킬 사용
curl -L 'https://api.openai.com/v1/responses' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"tools": [
{
"type": "shell",
"environment": {
"type": "local",
"skills": [
{
"name": "csv-insights",
"description": "Summarize CSV files and produce a markdown report.",
"path": "<path-to-skill-folder>"
}
]
}
}
],
"input": "Use the csv-insights skill and run locally to summarize today\'s CSV reports in this repo."
}'
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
tools: [
{
type: "shell",
environment: {
type: "local",
skills: [
{
name: "csv-insights",
description: "Summarize CSV files and produce a markdown report.",
path: "<path-to-skill-folder>",
},
],
},
},
],
input:
"Use the csv-insights skill and run locally to summarize today's CSV reports in this repo.",
});
console.log(response.output_text);
response = client.responses.create(
model="gpt-6-astra",
tools=[
{
"type": "shell",
"environment": {
"type": "local",
"skills": [
{
"name": "csv-insights",
"description": "Summarize CSV files and produce a markdown report.",
"path": "<path-to-skill-folder>",
}
],
},
}
],
input="Use the csv-insights skill and run locally to summarize today's CSV reports in this repo.",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{OfLocal: &responses.LocalEnvironmentParam{
Skills: []responses.LocalSkillParam{{
Name: "csv-insights",
Description: "Summarize CSV files and produce a markdown report.",
Path: "<path-to-skill-folder>",
}},
}},
}}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Use the csv-insights skill and run locally to summarize today's CSV reports in this repo.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.ResponseCreateParams;
import java.util.List;
import java.util.Map;
String skillPath = "<path-to-skill-folder>";
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Use the csv-insights skill to summarize today's CSV reports.")
.putAdditionalBodyProperty(
"tools",
JsonValue.from(
List.of(
Map.of(
"type",
"shell",
"environment",
Map.of(
"type",
"local",
"skills",
List.of(
Map.of(
"name", "csv-insights",
"description",
"Summarize CSV files and produce a Markdown report.",
"path", skillPath)))))))
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Use the csv-insights skill to summarize today's CSV reports.",
tools: [
{
type: :shell,
environment: {
type: :local,
skills: [
{
name: "csv-insights",
description: "Summarize CSV files and produce a Markdown report.",
path: "<path-to-skill-folder>"
}
]
}
}
]
)
puts(response.output_text)
Agents API
Agents API에서 스킬을 사용하려면 스킬 디렉터리를 샌드박스에 넣고, 세션을 만들 때 environment.capability_directories에 상위 디렉터리를 등록하세요. 이들은 capability directories라고 불러요. 하네스는 이를 사용해 스킬을 발견하며, 이 설정은 호스티드 셸의 skill_reference 연결 형식을 사용하지 않아요.
예를 들어 샌드박스에 계약 검토 스킬과 풀 리퀘스트 검토 스킬을 배치한다면:
/workspace/capabilities/
├── legal/
│ └── contract-redline/
│ ├── SKILL.md
│ └── references/
│ └── fallback-clauses.md
└── engineering/
└── review-pr/
├── SKILL.md
└── references/
└── review-guidelines.md
세션 생성 요청에 이 환경 구성을 사용하세요.
{
"environment": {
"type": "self_hosted",
"workspace_directory": "/workspace",
"capability_directories": [
"/workspace/capabilities/legal",
"/workspace/capabilities/engineering"
]
}
}
Capability 디렉터리에는 다음 요구 사항이 있어요.
- 경로는 샌드박스 안의 디렉터리를 가리켜야 해요.
- 경로는 절대적이고 고유해야 하며,
.또는..경로 세그먼트를 포함할 수 없어요. - 세션은 최대 32개의 capability 디렉터리를 등록할 수 있어요.
- 디렉터리는 환경에 이미 존재해야 해요.
샌드박스가 사용 가능해지면 하네스는 이 디렉터리에서 SKILL.md 파일을 검색하고 발견된 각 스킬의 이름과 설명을 컨텍스트에 추가해요. 모델은 관련 스킬을 선택하고 전체 지시와 지원 파일을 읽을 수 있어요.
세션 설정은 Agent 구성, 실행 환경은 샌드박스 연결을 참고하세요. 에이전트에 제공하기 전에 스킬과 지원 파일을 검토하고 샌드박스 보안 지침을 따르세요.
사용자 프롬프트에서의 스킬
Responses API 셸 도구의 경우 플랫폼은 사용 가능한 각 스킬의 name, description, path를 사용자 프롬프트 컨텍스트에 추가해서 모델이 스킬 존재를 알게 해요.
모델은 이 메타데이터를 바탕으로 스킬을 호출할지 결정해요. 모델이 스킬을 호출하면 path를 사용해 SKILL.md에서 전체 Markdown 지시를 읽어요.
스킬 지시는 사용자 프롬프트 입력(시스템 프롬프트 입력이 아님)이므로 다른 사용자 제공 지시와 같은 우선순위로 처리돼요. 명시적인 제어를 원하면 여전히 모델에게 "<skill name> 스킬을 사용해"라고 지시할 수 있어요.
한도와 검증
SKILL.md파일 매칭은 대소문자를 구분하지 않아요.- 스킬 번들에는
skill.md/SKILL.md파일이 정확히 하나만 허용돼요. - 스킬 프론트매터 검증은 agent skills 명세를 따르요.
- 최대 zip 업로드 크기는
50 MB예요. - 스킬 버전당 최대 파일 수는
500이에요. - 최대 압축 해제 파일 크기는
25 MB예요.
네트워크 접근과 함께하는 안전
Responses API와 함께 사용하는 모든 스킬을 검사하는 것은 매우 중요해요. 스킬은 프롬프트 인젝션 기반 데이터 유출 같은 보안 위험을 도입해요. 이 도구를 사용하기 전에 아래 위험과 안전 섹션을 신중히 검토하세요.
버전 관리와 관리
버전 포인터
- 버전이 제공되지 않으면
default_version이 사용돼요. latest_version은 가장 최근 업로드를 추적해요.skill_reference.version은 정수 또는"latest"를 받아요.
새 버전 생성
새 스킬 버전 생성
curl -X POST 'https://api.openai.com/v1/skills/<skill_id>/versions' \
-H "Authorization: Bearer ***" \
-F 'files=@./geometry.zip;type=application/zip'
기본 버전 설정
스킬의 기본 버전 설정
curl -X POST 'https://api.openai.com/v1/skills/<skill_id>' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{"default_version": 2}'
삭제 규칙
- 기본 버전은 삭제할 수 없어요. 먼저 다른 기본을 설정하세요.
- 마지막 남은 버전을 삭제하면 스킬이 삭제돼요.
- 스킬을 삭제하면 모든 버전도 함께 제거돼요.
큐레이티드 스킬 (Curated skills)
OpenAI는 id로 참조할 수 있는 일련의 퍼스트파티 스킬을 유지 관리해요 (예: openai-spreadsheets).
큐레이티드 스킬 참조
{ "type": "skill_reference", "skill_id": "openai-spreadsheets", "version": "latest" }
인라인 스킬 (Inline skills)
호스티드 스킬을 만들고 싶지 않다면 환경의 skills 배열에 zip 번들을 (base64로) 인라인할 수 있어요.
스킬 번들 인라인
INLINE_ZIP=$(base64 -i ./basic_math.zip)
curl -L 'https://api.openai.com/v1/containers' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"name": "inline-skill-container",
"skills": [
{
"type": "inline",
"name": "basic_math",
"description": "Add or multiply numbers.",
"source": {
"type": "base64",
"media_type": "application/zip",
"data": "'"$INLINE_ZIP"'"
}
}
]
}'
위험과 안전 (Risks and safety)
Responses API와 함께 사용하는 스킬을 검사하는 것이 중요해요. 스킬은 프롬프트 인젝션 기반 데이터 유출 같은 보안 위험을 도입해요.
네트워크 접근과 함께 사용하는 스킬은 네트워킹 위험·안전 섹션을 신중히 검토하세요.
스킬을 특권 코드와 지시로 취급
스킬 콘텐츠는 계획, 도구 사용, 명령 실행에 영향을 줄 수 있어요. 어떤 스킬도 개발자가 검증할 때까지 잠재적으로 신뢰할 수 없는 입력으로 검토해야 해요.
오픈 스킬 저장소를 최종 사용자에게 노출하지 마세요
소비자 최종 사용자가 공개 카탈로그에서 임의의 스킬을 자유롭게 탐색·선택·연결할 수 있는 제품 설계를 피하세요. 이는 다음으로 인한 위험을 실질적으로 증가시켜요.
- 악성
SKILL.md지시를 통한 프롬프트 인젝션과 정책 우회 - 심사되지 않은 자동화로 촉발되는 데이터 유출 또는 파괴적 액션
개발자 수준에서 스킬 통합
스킬은 개발자가 검사·통합한 다음, 경계가 있는 제품 경험을 통해서만 최종 사용자에게 노출해야 해요. 실제로는:
- 스킬을 특정 제품 워크플로/사용 사례에 매핑하세요.
- 임의의 스킬 선택에 대한 최종 사용자 통제를 막으세요.
- 쓰기 또는 고영향 액션을 명시적 승인과 정책 검사 뒤에 두세요.
민감한 액션에 승인 요구
쓰기 또는 고영향 액션을 수행할 수 있는 워크플로라면 실행 전에 명시적 승인을 요구하세요.
데이터 상주와 보존 요구 사항 검증
Responses API는 두 가지 폼 팩터로 스킬을 지원해요: 로컬 실행과 호스티드 컨테이너 기반 실행. 호스티드 스킬은 호스티드 셸과 같은 컨테이너 생애 주기를 따라요: 마운트된 스킬과 컨테이너 파일은 컨테이너가 활성인 동안 사용 가능하고, 컨테이너가 만료·삭제되면 폐기돼요. 실행을 전적으로 여러분이 관리하는 인프라에 두려면 로컬 셸 모드를 사용하세요. Agents API 샌드박스는 샌드박스 생애 주기를 참고하세요. 데이터 통제에 대해 더 읽어보세요.
더 알아보기 (Learn more)
- Shell 가이드에서 호스티드·로컬 셸 실행을 확인하세요.
- Agent Skills 표준과 명세를 참고하세요.
- 데이터 통제에서 ZDR 및 데이터 상주를 알아보세요.