Skip to content

skill_agent

Level 2 · Tools and Skills — managing reusable skills with SkillStore.

A skill is a named, reusable instruction block the agent can pull into its prompt on demand. This example populates an InMemorySkillStore, filters skills by relevance, and formats a matched set for prompt injection — the three operations every skill-driven agent performs.

  • Lines: ~42
  • Prerequisites: none
  • Key concepts: SkillStore, Skill, filter_skills, format_skills_list

Run it

Installed via pip? Copy this example into your project with the CLI (no checkout needed):

pip install "symfonic-core[cli]"
symfonic examples add skill_agent
python -m skill_agent

Or, from a source checkout (run from the repo root):

python -m examples.skill_agent

Expected output:

Skills: ['summarize', 'translate', 'review']
Filtered 'code': ['review']
Prompt injection (<n> chars):
<formatted skill list>

Full code

"""Skill agent -- demonstrates SkillStore + skill-driven agent behavior.

Uses InMemorySkillStore to register and filter skills. <=50 lines,
InMemory only, zero external network calls.
Usage: python -m examples.skill_agent
"""

import asyncio

from symfonic.core import BaseAgentDeps
from symfonic.core.models import Skill
from symfonic.core.protocols import SkillStore
from symfonic.core.skills.filter import filter_skills
from symfonic.core.skills.formatter import format_skills_list
from symfonic.core.skills.in_memory import InMemorySkillStore
from symfonic.core.testing import MockModelProvider


async def main() -> None:
    # 1. Populate skill store
    store = InMemorySkillStore()
    await store.create(Skill(name="summarize", description="Summarize text", content="Extract key points."))
    await store.create(Skill(name="translate", description="Translate text", content="Convert languages."))
    await store.create(Skill(name="review", description="Code review", content="Find bugs."))

    # 2. Wire deps
    deps = BaseAgentDeps(ModelProvider=MockModelProvider())
    deps.register(SkillStore, store)

    # 3. Demonstrate skill-driven behavior
    all_skills = await store.list()
    print(f"Skills: {[s.name for s in all_skills]}")

    matched = filter_skills(all_skills, "code")
    print(f"Filtered 'code': {[s.name for s in matched]}")

    formatted = format_skills_list(all_skills)
    print(f"Prompt injection ({len(formatted)} chars):\n{formatted}")


if __name__ == "__main__":
    asyncio.run(main())

Step by step

1. Populate the skill store

store = InMemorySkillStore()
await store.create(Skill(name="summarize", description="Summarize text", content="Extract key points."))
await store.create(Skill(name="translate", description="Translate text", content="Convert languages."))
await store.create(Skill(name="review", description="Code review", content="Find bugs."))

A Skill has a name, a short description (used for matching), and a content block (the instruction injected into the prompt). InMemorySkillStore is the zero-infrastructure implementation of the SkillStore protocol — create is async because real stores hit a database.

2. Register the store as a dependency

deps = BaseAgentDeps(ModelProvider=MockModelProvider())
deps.register(SkillStore, store)

Just like documents and tools, the skill store is injected by protocol. An agent node asks deps for a SkillStore; it neither knows nor cares that it's the in-memory one.

3. List all skills

all_skills = await store.list()
print(f"Skills: {[s.name for s in all_skills]}")

list() returns every stored skill. This is what a "show my capabilities" query would enumerate.

4. Filter by relevance

matched = filter_skills(all_skills, "code")
print(f"Filtered 'code': {[s.name for s in matched]}")   # ['review']

filter_skills narrows a skill set to those relevant to a query string. Here only review (description "Code review") matches "code". In an agent turn you'd pass the user's message so the prompt carries just the pertinent skills — not the whole library — keeping token cost down.

5. Format for prompt injection

formatted = format_skills_list(all_skills)
print(f"Prompt injection ({len(formatted)} chars):\n{formatted}")

format_skills_list renders skills into the text block the prompt builder splices into the system prompt. This is the bridge between stored skills and what the model actually sees. In a full agent this is wired via a SkillsSection — see e2e_basic_agent.

The pattern

Store many → filter to the relevant few → format into the prompt. This keeps the prompt focused: the agent has access to a large skill library but only pays for the handful each turn actually needs.

What to try next

See also