{ "cells": [ { "cell_type": "markdown", "id": "e94e5218", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "# Skills in OpenAI API\n", "\n", "Upload, manage, and attach reusable skills to hosted environments. Agent Skills let you upload and reuse versioned bundles of files in hosted and local shell environments. For the full reference, see the [Skills documentation](https://developers.openai.com/api/docs/guides/tools-skills)." ] }, { "cell_type": "markdown", "id": "a2de1700", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "## What is a skill?" ] }, { "cell_type": "markdown", "id": "00869b5b", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "A skill is a reusable bundle of files (instructions + scripts + assets), packaged as a folder and anchored by a required `SKILL.md` manifest. OpenAI copies that bundle into an execution environment so the model can read instructions and run code as needed.\n", "\n", "In hosted shell, here's what happens when you attach skills to the shell tool environment (`environment.type=\"container_auto\"`):\n", "\n", "\n", "- The service uploads and unzips skills into the runtime\n", "- The service reads `SKILL.md` frontmatter (name/description), then adds each skill’s `name`, `description`, and `path` to the hidden system prompt context, which lets the model know the skill exists\n", "- If the model decides to invoke a skill, it uses the `path` to read `SKILL.md`, then explores files and executes scripts via the shell tool\n", "\n", "Skills are for procedures: repeatable workflows where the _how_ matters (steps, branching logic, formatting rules, scripts). Skills are useful for when you want your procedure:\n", "\n", "- Reused across prompts/agents\n", "- Versioned and independently shipped\n", "- Invoked only when needed (not baked into every system prompt)" ] }, { "cell_type": "markdown", "id": "3a645e42", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "### When to use skills\n" ] }, { "cell_type": "markdown", "id": "d1c7dde6", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "**Skills are particularly appropriate and powerful when…**" ] }, { "cell_type": "markdown", "id": "37ccbc6b", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "1. **You want a reusable, independently versionable set of behaviors.**\n", "Examples: “PowerPoint formatting procedure,” “company-specific report generator,” “standard data-cleaning pipeline.”\n", "2. **Your workflow is highly conditional, or branches like a complex flow chart.**\n", "Example: If X → do this; else if Y → do that; plus validation + retries.\n", "3. **Your workflow needs code execution and local artifacts.**\n", "Anything that benefits from scripts, templates, test fixtures, or reference assets that should live beside the instructions. Skills are designed as a zip of those resources.\n", "4. **You want to keep system prompts slim.**\n", "Put stable procedures in skills; keep system prompts for global behavior.\n", "5. **Multiple agents or teams share the same “house style.”**\n", "Skills are a nice “org standard library” pattern.\n", "6. **You need reproducibility**\n", "Skills are naturally compatible with version pinning via skill versions (see versioning section below).\n" ] }, { "cell_type": "markdown", "id": "225f9d8d", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "**Skills are less ideal when…**\n", "\n", "- It’s truly a **one-off** task (a quick inline script in the conversation is fine).\n", "- You mostly need **live external data or side effects.** (That’s a tool/API call).\n", "- The procedure changes every day (skills shine when the workflow stabilizes).\n" ] }, { "cell_type": "markdown", "id": "543edbeb", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "## Skills vs. tools vs. system prompts" ] }, { "cell_type": "markdown", "id": "6749678a", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "System prompts and tool schemas become heavy when the boundary isn’t crisp. Use all three to stay organized and help models perform better. Here’s a simple framework:" ] }, { "cell_type": "markdown", "id": "f8c0a576", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "**System prompt: global behavior and constraints**\n", "\n", "Use for:\n", "- Safety boundaries, tone, refusal style\n", "- “Always do X” principles that apply every turn\n", "- Small, stable policies\n", "\n", "Avoid:\n", "\n", "- Putting long, multi-step procedures here (it bloats every turn and becomes brittle)\n", "\n" ] }, { "cell_type": "markdown", "id": "b463f5d4", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "**Tools: “do something in the world”**\n", "\n", "Use tools when the model must:\n", "\n", "- Call external services or databases\n", "- Create side effects (tasks outside of the environment, like canceling an order or sending an email)\n", "- Fetch live state\n", "\n", "Tools should:\n", "\n", "- Be narrowly scoped\n", "- Have strongly typed inputs\n", "- Be explicit about side effects\n", "\n", "**Skills: packaged procedures (+ code + assets)**\n", "\n", "Use a skill when you want the model to:\n", "\n", "- Follow a repeatable workflow\n", "- Use scripts/templates\n", "- Execute code in a sandbox\n", "- Do it sometimes, not always\n" ] }, { "cell_type": "markdown", "id": "424d0a4e", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "## Skill packaging: SKILL.md and folder layout" ] }, { "cell_type": "markdown", "id": "26ef7aee", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "### Folder structure\n", "\n", "A skill is just a folder bundle. Here's an example:\n", "- `SKILL.md` (required)\n", "- Scripts, like `*.py`, `*.js` (optional)\n", "- Helpers and `requirements.txt`\n", "- Assets, templates, sample inputs\n" ] }, { "cell_type": "markdown", "id": "8214a412", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "### SKILL.md frontmatter\n", "\n", "OpenAI models expect names and descriptions to come from frontmatter (important for discovery and routing). Put name and description in the `SKILL.md` frontmatter. Use each API `create` call to upload one skill bundle (one top-level folder) containing exactly one `SKILL.md`/`skill.md`. To upload multiple skills, upload multiple bundles.\n" ] }, { "cell_type": "markdown", "id": "9c04adb2", "metadata": {}, "source": [ "## Creating skills via API\n", "\n", "After you assemble your skill in a folder, create the skill with an API call.\n", "\n", "### Directory upload or zip upload\n", "\n", "\n", "Use `POST /v1/skills` to upload and validate your skill, extracting name and description from the manifest frontmatter. You can either upload a zip bundle or upload multiple files in your request.\n", "\n", "**Option A: Upload files (multipart)**\n", "\n" ] }, { "cell_type": "markdown", "id": "54e51104", "metadata": {}, "source": [ "```\n", "curl -X POST 'https://api.openai.com/v1/skills' \\\n", " -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n", " -F 'files[]=@./csv_insights_skill/SKILL.md;filename=csv_insights_skill/SKILL.md;type=text/markdown' \\\n", " -F 'files[]=@./csv_insights_skill/calculate.py;filename=csv_insights_skill/calculate.py;type=text/plain'\n", "```" ] }, { "cell_type": "markdown", "id": "26ba16a4", "metadata": {}, "source": [ "**Option B: Upload zip**\n", "\n", "```\n", "curl -X POST 'https://api.openai.com/v1/skills' \\\n", " -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n", " -F 'files=@./csv_insights_skill.zip;type=application/zip'\n", "```\n", "\n", "If you hit server errors, **zip locally and upload the zip**. We ran into this internally and found this to be a practical workaround.\n", "\n" ] }, { "cell_type": "markdown", "id": "ef28c1db", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "**Skill object and version pointers**" ] }, { "cell_type": "markdown", "id": "9a235f2e", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "A skill returns identifiers and version pointers (e.g., default, latest). Version pointers show up in platform changes and tests." ] }, { "cell_type": "markdown", "id": "aac5b394", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "## Mounting skills into execution\n", "\n", "Models use skills via the shell and container. To use skills in the Responses API, attach them to the shell tool with `tools[].environment.skills`.\n", "\n", "### How to reference skills\n", "\n", "Specify the environment, either hosted or local shell.\n", "**Hosted vs. local**\n", "\n", "- Hosted shell: `environment.type=\"container_auto\"`\n", "- Local shell: `environment.type=\"local\"`\n", "\n", "**Skills can be referenced as**:\n", "\n", "- `skill_reference` (by `skill_id`, optionally with `version` or `\"latest\"`)\n", "- `inline` (base64 zip bundle) when you don’t want to create a hosted skill" ] }, { "cell_type": "markdown", "id": "fc045bd4", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "## Runnable example: `csv_insights_skill` Skill" ] }, { "cell_type": "markdown", "id": "7226df70", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "**1) Create the skill folder.**\n", "\n", "```\n", "csv_insights_skill/\n", "├── SKILL.md\n", "├── requirements.txt\n", "├── run.py\n", "└── assets/\n", " └── example.csv\n", "```\n" ] }, { "cell_type": "markdown", "id": "a234980f", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "**2) Create your `SKILL.md`**" ] }, { "cell_type": "markdown", "id": "1d68adf0", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "```\n", "---\n", "name: csv-insights\n", "description: Summarize a CSV, compute basic stats, and produce a markdown report + a plot image.\n", "---\n", "\n", "# CSV Insights Skill\n", "\n", "## When to use this\n", "Use this skill when the user provides a CSV file and wants:\n", "- a quick summary (row/col counts, missing values)\n", "- basic numeric statistics\n", "- a simple visualization\n", "- results packaged into an output folder (or zip)\n", "\n", "## Inputs\n", "- A CSV file path (local) or a file mounted in the container.\n", "\n", "## Outputs\n", "- `output/report.md`\n", "- `output/plot.png`\n", "\n", "## How to run\n", "\n", "python -m pip install -r requirements.txt\n", "python run.py --input assets/example.csv --outdir output\n", "\n", "```" ] }, { "cell_type": "markdown", "id": "30d04c56", "metadata": {}, "source": [ "**3) Create your `run.py`**" ] }, { "cell_type": "code", "execution_count": null, "id": "f4ae6cfa", "metadata": {}, "outputs": [], "source": [ "import argparse\n", "from pathlib import Path\n", "\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "\n", "def write_report(df: pd.DataFrame, outpath: Path) -> None:\n", " lines = []\n", " lines.append(f\"# CSV Insights Report\\n\")\n", " lines.append(f\"**Rows:** {len(df)} \\n**Columns:** {len(df.columns)}\\n\")\n", " lines.append(\"\\n## Columns\\n\")\n", " lines.append(\"\\n\".join([f\"- `{c}` ({df[c].dtype})\" for c in df.columns]))\n", "\n", " missing = df.isna().sum()\n", " if missing.any():\n", " lines.append(\"\\n## Missing values\\n\")\n", " for col, count in missing[missing > 0].items():\n", " lines.append(f\"- `{col}`: {int(count)}\")\n", " else:\n", " lines.append(\"\\n## Missing values\\nNo missing values detected.\\n\")\n", "\n", " numeric = df.select_dtypes(include=\"number\")\n", " if not numeric.empty:\n", " lines.append(\"\\n## Numeric summary (describe)\\n\")\n", " lines.append(numeric.describe().to_markdown())\n", "\n", " outpath.write_text(\"\\n\".join(lines), encoding=\"utf-8\")\n", "\n", "\n", "def make_plot(df: pd.DataFrame, outpath: Path) -> None:\n", " numeric = df.select_dtypes(include=\"number\")\n", " if numeric.empty:\n", " # No numeric columns → skip plotting\n", " return\n", "\n", " # Plot the first numeric column as a simple histogram\n", " col = numeric.columns[0]\n", " plt.figure()\n", " df[col].dropna().hist(bins=30)\n", " plt.title(f\"Histogram: {col}\")\n", " plt.xlabel(col)\n", " plt.ylabel(\"Count\")\n", " plt.tight_layout()\n", " plt.savefig(outpath)\n", " plt.close()\n", "\n", "\n", "def main() -> None:\n", " parser = argparse.ArgumentParser()\n", " parser.add_argument(\"--input\", required=True, help=\"Path to input CSV\")\n", " parser.add_argument(\"--outdir\", required=True, help=\"Directory for outputs\")\n", " args = parser.parse_args()\n", "\n", " inpath = Path(args.input)\n", " outdir = Path(args.outdir)\n", " outdir.mkdir(parents=True, exist_ok=True)\n", "\n", " df = pd.read_csv(inpath)\n", "\n", " write_report(df, outdir / \"report.md\")\n", " make_plot(df, outdir / \"plot.png\")\n", "\n", "\n", "if __name__ == \"__main__\":\n", " main()\n" ] }, { "cell_type": "markdown", "id": "fa6ef039", "metadata": {}, "source": [ "**4) Zip it (recommended)**" ] }, { "cell_type": "markdown", "id": "c844695b", "metadata": {}, "source": [ "```\n", "zip -r csv_insights_skill.zip csv_insights_skill\n", "```" ] }, { "cell_type": "markdown", "id": "90ad52c8", "metadata": {}, "source": [ "**5) Upload the skill**\n", "\n", "```\n", "curl -X POST 'https://api.openai.com/v1/skills' \\\n", " -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n", " -F 'files=@./csv_insights_skill.zip;type=application/zip'\n", "```" ] }, { "cell_type": "markdown", "id": "07085491", "metadata": {}, "source": [ "**6) Run the skill via the API (hosted shell pattern)**\n", "\n", "\n", "This follows the flow: create skill → call Responses API with the shell tool, with `environment.skills` referencing the skill\n", "\n", "Conceptually:\n" ] }, { "cell_type": "code", "execution_count": null, "id": "1b00411d", "metadata": {}, "outputs": [], "source": [ "from openai import OpenAI\n", "client = OpenAI()\n", "\n", "response = client.responses.create(\n", " model=\"gpt-5.2\",\n", " tools=[{\n", " \"type\": \"shell\",\n", " \"environment\": {\n", " \"type\": \"container_auto\",\n", " \"skills\": [\n", " {\"type\": \"skill_reference\", \"skill_id\": \"\"},\n", " {\"type\": \"skill_reference\", \"skill_id\": \"\", \"version\": 2},\n", " ],\n", " },\n", " }],\n", " input=\"Use the skills to analyze the uploaded CSV and write outputs to /mnt/output.\"\n", ")\n", "\n", "print(response.output_text)" ] }, { "cell_type": "markdown", "id": "c926fc47", "metadata": {}, "source": [ "**7) Use this skill via the API (local container pattern)**\n", "\n", "Skills also work with local shell mode. The skill selection and prompt behavior are the same as hosted shell mode, but command execution and filesystem access are still handled by your local runtime.\n", "\n", "Conceptually:" ] }, { "cell_type": "code", "execution_count": null, "id": "5926eec2", "metadata": {}, "outputs": [], "source": [ "from openai import OpenAI\n", "\n", "client = OpenAI()\n", "\n", "response = client.responses.create(\n", " model=\"gpt-5.2\",\n", " tools=[\n", " {\n", " \"type\": \"shell\",\n", " \"environment\": {\n", " \"type\": \"local\",\n", " \"skills\": [\n", " {\"type\": \"skill_reference\", \"skill_id\": \"\"},\n", " {\"type\": \"skill_reference\", \"skill_id\": \"\", \"version\": 2},\n", " ],\n", " },\n", " }\n", " ],\n", " input=\"Use the configured skills and run locally to summarize today's CSV reports in this repo.\",\n", ")\n", "\n", "print(response.output_text)" ] }, { "cell_type": "markdown", "id": "fd7142da", "metadata": {}, "source": [ "## Operational best practices" ] }, { "cell_type": "markdown", "id": "1aaedd61", "metadata": {}, "source": [ "**1) Keep skills “discoverable”**\n", "\n", "* Put a **clear** `name` and `description` in frontmatter.\n", "* In `SKILL.md`, include: when to use, how to run, expected outputs, gotchas.\n", "\n", "- Add explicit routing guidance: “Use when…” vs. “Don’t use when…”, and a few key edge cases, all in `SKILL.md`.\n", "\n", "- Include negative examples (when the skill should *not* be triggered) alongside positive examples to improve routing accuracy.\n", "\n", "- If routing feels inconsistent, iterate on name, description, and examples before changing code.\n", "\n", "This came up in “bulk upload” discussions: name and description should come from frontmatter, and you should test with a small number first.\n", "\n", "**2) Prefer zip uploads for reliability and reproducibility**\n", "\n", "* Zips are portable, easy to version, and a useful workaround when uploads misbehave.\n", "\n", "**3) Version pin in production**\n", "\n", "You want to be able to say, “Run this procedure version,” not, “Run whatever the latest is.” Skills are trending toward explicit versions (**default_version**, **latest_version**), and there’s active work on version creation endpoints.\n", "\n", "* How to pin: `version: 2`\n", "* How to float: `version: \"latest\"`\n", "* What happens when omitted: defaults to `default_version`\n", "\n", "Consider pinning the model and skill version together for reproducible behavior across deployments.\n", "\n", "**4) Design skills like tiny CLIs**\n", "\n", "A good skill script:\n", "\n", "* Runs from the command line\n", "* Prints deterministic stdout\n", "* Fails loudly with usage/errors\n", "* Writes outputs to known file paths when needed\n", "\n", "Add concrete templates and worked examples inside the skill (inputs → commands → expected outputs); they cost nothing on turns where the skill isn’t invoked. When examples are workflow-specific, prefer examples and templates in skills over system-level, few-shot prompting.\n", "\n", "**5) Avoid duplicating skills in system prompts**\n", "\n", "If the system prompt repeats the entire procedure, people will:\n", "\n", "* Bypass skills\n", "* Stuff logic into tool schemas\n", "\n", "And you lose the whole point (reusability + versioning + conditional invocation) of skills. Keep the system prompt content separate.\n", "\n", "**6) Network access**\n", "\n", "Combining skills and open network access is high-risk. If you must use network access, use strict allowlists and treat tool output as untrusted. Avoid this configuration for consumer-facing apps where users expect confirmation controls.\n", "**If network access is required, pair allowlists with explicit “what data is allowed to leave” guidance.**\n", "\n", "**7) Use a model that reliably executes multi-step workflows**\n", "\n", "Skills work best when the model is strong at long-context reasoning and multi-step tool execution (filesystem navigation, CLI runs, verification).\n", "If you see partial completion or brittle execution, upgrade the model or simplify the workflow, and add explicit verification steps and output checks in `SKILL.md`." ] }, { "cell_type": "markdown", "id": "9f2a4774", "metadata": {}, "source": [ "**Limits and validation**\n", "\n", "- `SKILL.md` matching is case-insensitive\n", "- Exactly one manifest file allowed (`skill.md`/`SKILL.md`)\n", "- Frontmatter validation follows Agent Skills spec (name field)\n", "- Max zip upload size: 50 MB\n", "- Max file count per skill version: 500\n", "- Max uncompressed file size: 25 MB" ] }, { "cell_type": "markdown", "id": "6820cab7", "metadata": {}, "source": [ "## Conclusion\n", "\n", "Skills are the missing “middle layer” between prompts and tools: **prompts** define always-on behavior, **tools** provide atomic capabilities and side effects, and **skills** package repeatable procedures (instructions + scripts + assets) that the model can **mount and execute only when needed.**\n", "\n", "**Use skills to keep your system prompts lean and your workflows durable.** Start small—bundle one stable procedure with a clear `SKILL.md`, make it runnable as a tiny CLI, and ship it. After it’s in production, pin versions for reproducibility, iterate safely by publishing new versions, and treat your skills library like an internal standard library: audited, discoverable, and shared across agents.\n", "\n", "As users scale from single-turn assistants to long-running agents, skills help turn “prompt spaghetti” into **maintainable, testable, versioned workflows**—for building agentic behavior you can trust, reuse, and evolve over time.\n", "\n", "To get started with Skills, check out our [documentation](https://developers.openai.com/api/docs/guides/tools-skills)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }