> For the complete documentation index, see [llms.txt](/llms.txt).
> A full single-fetch corpus is available at [llms-full.txt](/llms-full.txt).
---
title: Give agents skills and project guidance
description: Load capabilities on demand with SKILL.md folders, and apply always-on project guidance with AGENTS.md, instead of stuffing everything into one large prompt.
last_verified: 2026-09-29
---

**Skills** give an agent a library of capabilities without bloating its prompt. Each skill is a folder with a `SKILL.md` file. Only the skill's name and description stay in the agent's context; when a task matches, the agent loads the full instructions on demand.

Skills work alongside **`AGENTS.md`**, which provides always-on project guidance. Use `AGENTS.md` for conventions that apply to every task, such as coding style or project structure, and use skills for capabilities the agent reaches for only when relevant.

## Write a skill

A skill is a folder with a `SKILL.md` file. The `name` and `description` fields at the top of the file are the only parts that stay in context; the body loads when the agent decides the skill is relevant.

```text
skills/
  pdf-extraction/
    SKILL.md
    scripts/
      extract.py
```

```markdown
---
name: pdf-extraction
description: Extract tables and text from PDF files
---

# PDF extraction

To extract content from a PDF:

1. Write the PDF bytes to `input.pdf` in the sandbox.
2. Run `scripts/extract.py input.pdf` to produce `output.json`.
3. Read `output.json` and summarize the tables it contains.
```

Because `SKILL.md` is plain markdown, skills written for other agent frameworks work in AGNT5 unchanged. The `skills/` folder ships alongside your worker code in the same deployment bundle.

## Give an agent skills

Pass a curated list of skill names and the directory pool they resolve against. Each agent sees only the skills you select, so its catalog and context cost stay small.



**Python:**

```python
from agnt5 import Agent, Sandbox

researcher = Agent(
    name="researcher",
    model="openai/gpt-4o-mini",
    instructions="Help the user analyze documents.",
    skills_dir="./skills",
    skills=["pdf-extraction", "sql-reporting"],
    sandbox=Sandbox(),
)

reporter = Agent(
    name="reporter",
    model="openai/gpt-4o-mini",
    instructions="Build reports from warehouse data.",
    skills_dir="./skills",
    skills=["sql-reporting"],
)
```





**TypeScript:**

```ts
import { Agent, LM, Sandbox } from '@agnt5/sdk';

const researcher = new Agent({
  name: 'researcher',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Help the user analyze documents.',
  skillsDir: './skills',
  skills: ['pdf-extraction', 'sql-reporting'],
  sandbox: new Sandbox(),
});

const reporter = new Agent({
  name: 'reporter',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Build reports from warehouse data.',
  skillsDir: './skills',
  skills: ['sql-reporting'],
});
```





**Go:**

`WithAgentSkillsFromDir(dir, names...)` selects named skills from the pool. Attach a sandbox with `WithAgentSandbox` when a skill bundles scripts the agent should run.

```go
import "github.com/agnt5dev/sdk-go/agnt5"

researcher, err := agnt5.NewAgent("researcher",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Help the user analyze documents."),
    agnt5.WithAgentSkillsFromDir("./skills", "pdf-extraction", "sql-reporting"),
    agnt5.WithAgentSandbox(agnt5.NewHTTPSandbox(os.Getenv("SANDBOX_ENDPOINT"), os.Getenv("SANDBOX_API_KEY"))),
)

reporter, err := agnt5.NewAgent("reporter",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Build reports from warehouse data."),
    agnt5.WithAgentSkillsFromDir("./skills", "sql-reporting"),
)
```



Two agents can draw different subsets from the same `skills/` pool. A name that isn't in the pool fails at construction with the list of available skills, so typos surface immediately.

## Run a skill's bundled scripts

If a **[sandbox](/docs/build/sandboxes.md)** is attached, loading a skill also copies its bundled files into the sandbox workspace under `skills/<name>/`. The agent can then run those files using sandbox tools without any extra setup. Without a sandbox, `load_skill` still returns the skill's instructions; running the bundled scripts requires one.

## How loading works

When an agent has skills, the SDK adds a `<skills>` catalog of names and descriptions to the system prompt and registers a built-in `load_skill` tool. When a task matches, the agent calls `load_skill("pdf-extraction")` and receives the full instructions for that skill. Only loaded skills consume context; everything else stays out of the prompt.

Each load emits a `skill.loaded` event carrying the skill name, instruction length, and how many bundled files were copied into the sandbox, so a run's timeline shows exactly which skills were pulled in and when. To observe skill loads in your own code, iterate the agent's event stream.



**Python:**

```python
from agnt5 import SkillLoaded

async for event in agent.stream("Analyze this PDF"):
    if isinstance(event, SkillLoaded):
        print(f"Loaded: {event.skill_name} ({event.instructions_length} chars)")
```





**TypeScript:**

```ts
for await (const event of agent.stream('Analyze this PDF')) {
  if (event.eventType === 'skill.loaded') {
    console.log(`Loaded: ${event.skillName} (${event.instructionsLength} chars)`);
  }
}
```





**Go:**

`Agent.Run` has no event iterator (see [Running an agent](/docs/build/agents.md#running-an-agent)); the `skill.loaded` event still reaches the run's journal through `ctx.Emit`, so it appears in the run timeline. To react in code after the run, look for `load_skill` calls in `result.ToolCallDetails`.

```go
result, err := agent.Run(ctx, agnt5.AgentInput{Message: "Analyze this PDF"})
if err != nil {
    return err
}
for _, call := range result.ToolCallDetails {
    if call.Name == "load_skill" {
        fmt.Printf("Loaded: %v\n", call.Arguments["skill_name"])
    }
}
```



## Load every skill in a pool

Omit `skills` to load every skill in the directory. This is convenient for a single agent that should have the whole pool.



**Python:**

```python
agent = Agent(
    name="generalist",
    model="openai/gpt-4o-mini",
    instructions="Use whichever skill fits the task.",
    skills_dir="./skills",
)
```





**TypeScript:**

```ts
const agent = new Agent({
  name: 'generalist',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Use whichever skill fits the task.',
  skillsDir: './skills',
});
```





**Go:**

Omit the names and `WithAgentSkillsFromDir` loads every skill in the directory.

```go
agent, err := agnt5.NewAgent("generalist",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Use whichever skill fits the task."),
    agnt5.WithAgentSkillsFromDir("./skills"),
)
```



You can also load individual skills by path when you don't want a shared pool.



**Python:**

```python
from agnt5 import Agent, Skill

agent = Agent(
    name="analyst",
    model="openai/gpt-4o-mini",
    instructions="Analyze documents.",
    skills=[Skill.from_path("./my-skills/pdf-extraction")],
)
```





**TypeScript:**

```ts
import { Agent, LM, Skill } from '@agnt5/sdk';

const agent = new Agent({
  name: 'analyst',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Analyze documents.',
  skills: [Skill.fromPath('./my-skills/pdf-extraction')],
});
```





**Go:**

```go
pdfExtraction, err := agnt5.SkillFromPath("./my-skills/pdf-extraction")
if err != nil {
    return err
}

agent, err := agnt5.NewAgent("analyst",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Analyze documents."),
    agnt5.WithAgentSkills(pdfExtraction),
)
```

`SkillFromPath` accepts a skill directory or a direct `SKILL.md` path, and `WithAgentSkills` takes any number of loaded `Skill` values.



### Inspect the pool directly

Use `discover_skills` / `discoverSkills` / `agnt5.DiscoverSkills` to inspect the pool without constructing an agent, for example to list available skills at startup or validate the directory.



**Python:**

```python
from agnt5 import discover_skills

pool = discover_skills("./skills")
for name, skill in pool.items():
    print(f"{name}: {skill.description}")
```





**TypeScript:**

```ts
import { discoverSkills } from '@agnt5/sdk';

const pool = discoverSkills('./skills');
for (const [name, skill] of pool) {
  console.log(`${name}: ${skill.description}`);
}
```





**Go:**

```go
pool, err := agnt5.DiscoverSkills("./skills")
if err != nil {
    return err
}
for name, skill := range pool {
    fmt.Printf("%s: %s\n", name, skill.Description)
}
```

`DiscoverSkills` returns a `map[string]Skill` and skips malformed entries, so one bad folder doesn't hide the rest of the pool.



## Apply always-on project guidance with AGENTS.md

`AGENTS.md` is always-on guidance loaded into every prompt, so use it for conventions that always apply. Point the agent at a file, a directory (which uses its `AGENTS.md`), or a list of paths where later entries are more specific than earlier ones.



**Python:**

```python
agent = Agent(
    name="researcher",
    model="openai/gpt-4o-mini",
    instructions="Help the user analyze documents.",
    agents_md="./AGENTS.md",
    skills_dir="./skills",
    skills=["pdf-extraction"],
)
```





**TypeScript:**

```ts
const agent = new Agent({
  name: 'researcher',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Help the user analyze documents.',
  agentsMd: './AGENTS.md',
  skillsDir: './skills',
  skills: ['pdf-extraction'],
});
```





**Go:**

`WithAgentGuidance` takes files or directories (a directory resolves to its `AGENTS.md`); sources that don't exist are skipped.

```go
agent, err := agnt5.NewAgent("researcher",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Help the user analyze documents."),
    agnt5.WithAgentGuidance("./AGENTS.md"),
    agnt5.WithAgentSkillsFromDir("./skills", "pdf-extraction"),
)
```



To pass multiple files, use a list. The agent reads them in order, so put the most general file first and the most specific last.



**Python:**

```python
agent = Agent(
    name="researcher",
    model="openai/gpt-4o-mini",
    instructions="Help the user analyze documents.",
    agents_md=["./AGENTS.md", "./research/AGENTS.md"],
)
```





**TypeScript:**

```ts
const agent = new Agent({
  name: 'researcher',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Help the user analyze documents.',
  agentsMd: ['./AGENTS.md', './research/AGENTS.md'],
});
```





**Go:**

```go
agent, err := agnt5.NewAgent("researcher",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Help the user analyze documents."),
    agnt5.WithAgentGuidance("./AGENTS.md", "./research/AGENTS.md"),
)
```



Guidance loads before the skills catalog, so the agent reads its standing rules before the list of on-demand capabilities. To automatically merge a root `AGENTS.md` with any directory-specific ones, use `discover_agents_md` / `discoverAgentsMd` / `agnt5.DiscoverAgentsMD` and pass the result.



**Python:**

```python
from agnt5 import Agent, discover_agents_md

agent = Agent(
    name="researcher",
    model="openai/gpt-4o-mini",
    instructions="Help the user analyze documents.",
    agents_md=discover_agents_md("."),  # root AGENTS.md first, most specific last
)
```





**TypeScript:**

```ts
import { Agent, LM, discoverAgentsMd } from '@agnt5/sdk';

const agent = new Agent({
  name: 'researcher',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Help the user analyze documents.',
  agentsMd: discoverAgentsMd('.'), // root AGENTS.md first, most specific last
});
```





**Go:**

```go
guidance, err := agnt5.DiscoverAgentsMD(".", true) // root AGENTS.md first, most specific last
if err != nil {
    return err
}

agent, err := agnt5.NewAgent("researcher",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Help the user analyze documents."),
    agnt5.WithAgentGuidance(guidance...),
)
```



`discover_agents_md` / `discoverAgentsMd` walks upward from the start directory and stops at the repository root (the directory containing `.git`), so it never reads files outside your project. Go's `DiscoverAgentsMD(startDir, stopAtGit)` stops there only when `stopAtGit` is `true`; pass `false` to keep walking to the filesystem root.

## When to use which

| Use | For | Loaded |
|-----|-----|--------|
| `AGENTS.md` | Standing rules that apply to every task | Always, in every prompt |
| Skills | Capabilities the agent reaches for occasionally | On demand, when a task matches |

Both are optional. An agent with neither behaves exactly as before. Guidance and skills only change the prompt when you configure them.
