docs: documented graph-based agent llm skills usage

2026-06-02 12:45:13 -06:00
parent b1be15c9af
commit 2cbefe74e3
2 changed files with 140 additions and 0 deletions
+56
@@ -68,6 +68,10 @@ global_tools: # global tools available to nodes
- web_search_coyote.sh
mcp_servers: # MCP servers available to nodes
- pubmed-search
skills_enabled: true # optional; master switch for skills in `llm` nodes
enabled_skills: # optional; the *universe* of skills referenceable by any llm node
- code-review
- git-master
conversation_starters: # suggested prompts in the UI
- "Research WebAssembly outside of the browser"
variables: # optional; agent variables
@@ -138,6 +142,13 @@ nodes:
write in the same super-step (the validator catches missing reducers at
load time). See [Parallel Execution](#parallel-execution) for the full
reducer reference and merge semantics.
- **`skills_enabled` / `enabled_skills`:** Optional [skills](Skills) policy
for the graph. `skills_enabled: false` turns skills off for every `llm`
node in the graph regardless of node-level settings. `enabled_skills` is
the *universe*: the set of skill names any `llm` node may reference in
its own `enabled_skills`. Subset-violations are caught by the validator at
load time. See [Skills in Graph Agents](Skills#skills-in-graph-agents) for
the full per-node story.
### `{{initial_prompt}}`: Automatically Seeded
@@ -458,6 +469,51 @@ failure message containing one of: `timed out`, `rate limit`, `429`,
other error fails immediately without consuming further attempts. The
default is `1` (no retries).
### Skills on `llm` nodes
`llm` nodes are the only place [skills](Skills) attach inside a graph. Two
optional fields, mirroring the same fields on roles and agents:
```yaml
implement:
type: llm
prompt: "{{plan_summary}}"
tools: [fs_write, fs_patch, fs_cat]
skills_enabled: true # optional; default inherits from graph level
enabled_skills: # optional per-node restriction; must be a subset
- code-review # of graph-level enabled_skills
- verification-gates
```
Semantics (discovery-only, no auto-load):
- **`skills_enabled`:** Per-node override of the skills master switch.
`false` disables skills for this node only: no `skill__list` /
`skill__load` / `skill__unload` meta-tools are exposed to the model.
`true` or omitted inherits the graph-level / agent-level setting.
- **`enabled_skills`:** Per-node restriction of which skills the model can
see and load. The graph-level `enabled_skills` is the universe; the
per-node list must be a subset (the validator catches violations at
load time). When set, `skill__list` shows only these skills and
`skill__load` only accepts these names.
- **The model loads what it needs.** Nothing is auto-loaded. While the
node runs, the model uses `skill__list` to discover what's available
and `skill__load` to bring a skill's body / tools / MCP servers into
scope when it actually needs them. This matches the design intent of
skills as a dynamic capability layer.
- **Per-node policy isolation.** The node temporarily narrows the active
agent's skill policy to the node's `enabled_skills`. The original
policy is restored when the node finishes, so subsequent nodes see
their own policy. Any skills the model loaded during the node persist
into subsequent nodes by default; they're real registry insertions,
not node-scoped state. (Use the skill's own `auto_unload: true`
frontmatter for skills that should clean up automatically at turn
end.)
Agent nodes (which spawn full sub-agents) intentionally have no
`enabled_skills` field. The spawned child agent's own
`config.yaml`/`graph.yaml` declares its skill policy.
---
## rag
+84
@@ -198,6 +198,90 @@ Four validation points enforce these rules:
---
# Skills in Graph Agents
[Graph agents](Graph-Agents) integrate with the skill system at two levels: a graph-wide *universe* set at the top of
`graph.yaml`, and a per-node *auto-load* set on `llm` nodes. Other node types (script, approval, input, rag, map, end)
have no skill surface. Skills only apply where a model call happens.
## Graph level — the universe
The top-level `skills_enabled` and `enabled_skills` fields in `graph.yaml` work just like they do on a normal agent's
`config.yaml`: they declare the policy ceiling for the whole graph.
```yaml
name: coder
skills_enabled: true
enabled_skills:
- code-review
- git-master
- verification-gates
```
- **`skills_enabled: false`** at the graph level turns skills off for every `llm` node in the graph regardless of
node-level fields. Equivalent to "skills don't exist for this graph."
- **`enabled_skills`** at the graph level is the *universe*: the set of skill names any `llm` node in the graph is
allowed to reference in its own `enabled_skills`. The validator rejects per-node entries that aren't in this set at
load time.
- Omitting `enabled_skills` inherits whatever the role / global cascade resolves to (typically "all visible").
## Per-node: discovery-only on `llm` nodes
`llm` nodes can independently declare `skills_enabled` and `enabled_skills`. The graph-level field gates what's
*allowed* in the graph at all; the node-level field narrows that universe to what *this* node's model can see and
load. Nothing is auto-loaded. The model uses `skill__list` and `skill__load` to bring skills in as it needs them,
matching the rest of Coyote's skill model.
```yaml
nodes:
implement:
type: llm
prompt: "{{plan_summary}}"
tools: [fs_write, fs_patch, fs_cat]
enabled_skills: # must be a subset of graph-level enabled_skills
- code-review
- verification-gates
# skills_enabled: false # would disable skills for this node only
```
Semantics:
- **Discovery, not auto-load.** When the node runs, `skill__list` is exposed to the model along with `skill__load`
and `skill__unload`. The model decides which skills (if any) to load based on the task at hand. Loaded skills
compose into the system prompt and union tools/MCP servers via the standard `effective_role` pipeline; the
loading itself goes through the same `refresh_tool_scope` path as `.skill load` in the REPL.
- **Per-node policy.** When `enabled_skills` is set on the node, the graph's effective skill policy is temporarily
narrowed to that subset for the duration of the node. `skill__list` will only show those skills and `skill__load`
will only accept those names. When the node finishes, the original policy is restored so the next node sees its
own.
- **`skills_enabled: false`** at the node level is an off-switch: no `skill__*` meta-tools are exposed and no skills
can be loaded from this node. Useful when one node in an otherwise skill-enabled graph should run with a strict,
narrow context (for example a structured-output extraction step that shouldn't load editorial conventions).
- **Skill state carry-over.** Skills the model loads during a node persist into subsequent nodes (they're real
registry insertions, not node-scoped state). If you want a skill to clean up automatically at turn end, mark it
`auto_unload: true` in the skill's own frontmatter.
## Agent nodes have no skill surface
`agent` nodes spawn a full child agent (graph or normal). That child agent declares its own skill policy in its own
`config.yaml` / `graph.yaml`. Adding a skill field on the agent node would be redundant at best and surprising at
worst. The child author already controls what skills the child uses. So agent nodes intentionally don't accept
`enabled_skills` / `skills_enabled`.
## Validation
| Where | Check | Severity |
|----------------|------------------------------------------------------------------------------------------------------------|------------|
| Graph load | Per-node `enabled_skills` entries are non-empty strings | Hard error |
| Graph load | Per-node `enabled_skills` are a subset of graph-level `enabled_skills` (when set) | Hard error |
| Node execution | Each auto-loaded skill resolves to a real installed skill (otherwise that skill is skipped with a warning) | Warn |
| Node execution | Each auto-loaded skill's MCP / function-calling requirements are satisfied | Warn |
The validator only runs `validate_before_run` (the default); inspecting a graph via `coyote --info -a <name>` does
not trigger validation, so subset-violation errors surface on the first real run.
---
# Auto-unload
Skills with `auto_unload: true` in their frontmatter are removed from the registry at the end of each turn. Specifically,