A mini code agent with Docker Agent - Part 6: `skills`

6 min read

A skill is a procedure written in Markdown, of which only the name and the description are injected into the system prompt. The body itself is read only if the request matches it.

text
instruction           →  read on EVERY turn, for every question, useful or not
skill (name + desc)   →  ~2 lines in the system prompt
skill (body)          →  read ONLY when the request matches

That is what lets you give detailed procedures to a small model without "diluting" the one instruction it must always obey.

Turning skills on

yaml
agents:
  root:
    skills: true
    toolsets:
      - type: filesystem      # required: reading a skill means reading a file

skills: true loads every skill discovered on disk, starting from the current directory where you run docker-agent. The filesystem toolset is not optional: without it, the agent cannot read the SKILL.md files.

Where docker-agent goes looking for them

In our case it will be here:

text
06-skills/
└── .agents/skills/
    └── rustdoc/
        └── SKILL.md

The three forms of SKILL with docker-agent

1. A SKILL.md on disk

A YAML header, then the procedure in Markdown:

markdown
---
name: rustdoc
description: Write the rustdoc comment of a Rust function, in the team's house style.
---

# Documenting a Rust function

1. **One summary line**, imperative mood...
3. `# Errors` — required as soon as the function returns `Result`...
5. `# Examples` — required for every public item...

The description is what the model reads in order to decide: say what it is about, otherwise the skill will never be picked.

It is the only form that can bundle supporting files (read with read_skill_file): a template, an example, a script.

2. An inline skill, in the YAML

yaml
    skills:
      - name: changelog
        description: Write one Keep a Changelog entry from a description or a diff.
        instructions: |
          Produce a SINGLE changelog entry, Keep a Changelog style.
          Pick the category (Added, Changed, Fixed, Removed) and write one
          imperative sentence describing the user-visible change. Nothing else.

No file, no discovery: the skill travels inside the config. It is also always exposed (no filtering by name).

3. A fork skill, in an isolated sub-agent

yaml
      - name: file-summary
        description: Summarise a Rust source file in an isolated context.
        context: fork
        allowed_tools:
          - read_file
        instructions: |
          Read the file you were given, then answer with one sentence saying
          what it is for, then one bullet per public item. Change nothing.

By default, a skill is read in the current conversation: its procedure, and every tool call it triggers, pile up in the context. For a chatty procedure (reading a big file, running ten commands in a row, ...) that can be a problem on a small model (a context that grows too big).

context: fork makes it run in a sub-agent: a child session, its own history, and only its final answer comes back to the parent. The content of the file it read never pollutes the main conversation (but you start from a context with no history).

allowed_tools is an allow-list over the tools inherited from the parent (glob patterns accepted: read_*). Here, the sub-agent can read, and nothing else. Two fields reserved for fork mode — allowed_tools and toolsets are rejected at validation on a non-fork skill.

Invoking a skill

Three ways, from the most implicit to the most explicit:

  • Automatic: the model recognises that the request matches a description and loads the skill on its own.
  • By name: "use the rustdoc skill to...".
  • Slash/rustdoc, like a command.

If the small model you are using "struggles" with automatic detection: name the skill.

⚠️ A SKILL.md is executable content

Remember the security consequence above all: a skill is not documentation, it is code. Read a SKILL.md that came from somewhere else before dropping it into ~/.agents/skills/ — the mere fact that an agent reads it is enough to execute what it contains.

Filtering what gets loaded

skills: also accepts a list, and each entry is classified automatically: local or an http(s):// URL is a source, any other string is a skill name to keep.

yaml
skills:
  - local            # source: the skills on disk
  - rustdoc          # filter: keep only that one

The complete agent.yaml for this lesson

yaml
agents:
  root:
    model: mellum
    description: A Rust assistant that loads the team's procedures on demand.
    instruction: |
      You are a Rust assistant for this team.
      When a request matches one of your skills, follow that skill.
      Reply in French.

    skills:
      - local
      - rustdoc

      # --- An INLINE skill -----------
      - name: changelog
        description: Write one Keep a Changelog entry from a description or a diff.
        instructions: |
          Produce a SINGLE changelog entry, Keep a Changelog style.
          Pick the category (Added, Changed, Fixed, Removed) and write one
          imperative sentence describing the user-visible change. Nothing else.

      # --- A FORK skill: runs in an isolated sub-agent ---------------------
      - name: file-summary
        description: Summarise a Rust source file in an isolated context.
        context: fork
        allowed_tools:
          - read_file
        instructions: |
          Read the file you were given, then answer with one sentence saying
          what it is for, then one bullet per public item. Change nothing.

    toolsets:
      # REQUIRED for file-based skills: reading a SKILL.md is a file read.
      - type: filesystem
        allow_list:
          - "."

models:
  mellum:
    provider: dmr
    model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M
    base_url: http://localhost:12434/engines/v1
    # base_url: http://host.docker.internal:12434/engines/v1  # if you run the agent in a container

📝 You'll find all the configuration files over here: 06-skills

Running it

bash
cd 06-skills

# TUI
docker agent run localhost.agent.yaml

# Or TUI with sbx (sandbox):
sbx run docker-agent --kit . -- run agent.yaml

Prompts to try

Try these prompts in the TUI:

The on-disk skill: "document the parse_port function in the demo.rs file"

tui-01
tui-01

The inline skill: "/changelog Added support for u16 ports"

tui-02
tui-02

The fork skill: "make a summary demo.rs" or "/file-summary demo.rs"

tui-03
tui-03

What the agent can do: "What are your skills? Give the name and purpose of each one."

tui-04
tui-04

Key takeaways

  • A skill puts the name and the description in the system prompt, and keeps the body for later: that is how you give detailed procedures to a small model without lengthening its instruction.
  • The filesystem toolset is required for on-disk skills.
  • Three forms: SKILL.md, inline, fork.
  • On a small model, name the skill rather than hoping for automatic detection (but Mellum2 does rather well at it).
  • ⚠️ Treat a skill like a script, not like a note.

In the next blog post, we'll talk about instruction_file, to move the system prompt out of the YAML and into a Markdown file, and about commands, the slash shortcuts (/audit, /explain, ...).

Written by

0 Comments

No comments yet. Be the first to comment!

Copyright © 2026k33g_org's BlogPowered by Writizzy