Blog · AI agenti · MCP · skilly
Skill jako brána k toolům
Jak modelu nedávat sto toolů najednou. Načtení skillu odemkne tooly jednoho modulu spolu s návodem, jak s nimi pracovat, a nabídku zmenší až o 95 %.
Moje administrace e-shopu má MCP server pro AI agenty. Nabízí asi sto toolů: objednávky, faktury, dodací listy, sklad, Balíkobot, analytika, číselníky. Stejný registr toolů používá asistent přímo v UI, kde tooly volám in-process za přihlášeného uživatele. Nabídnout modelu všechny najednou se ukázalo jako špatný nápad. Řešením je udělat ze skillu bránu k toolům.
Problém
Nabídnout modelu všech sto toolů najednou znamená narazit na tři věci.
- Tokeny. Schémata a popisy toolů se posílají s každým krokem smyčky. Popisy mám záměrně dlouhé: čtyřdílné (co dělá, kdy použít, side-effecty, omezení) a každý parametr má popis s příkladem. Celý registr (95 toolů) by zabíral zhruba 96 000 tokenů, tedy průměrně ~1 000 na tool. Ty by se platily a zpracovávaly v každé iteraci smyčky, ještě před první otázkou uživatele.
- Přesnost výběru. Čím víc toolů model vidí, tím snáz sáhne vedle. Literatura tomu říká „prompt bloat“ (např. RAG-MCP, arXiv 2505.03275). Skilly pomáhají jen částečně: oddělí od sebe moduly, ale podobné tooly jako
orders_update,orders_items_updateaorders_set_statuszůstávají ve skillu orders pohromadě. - Chybějící postup. Samotný tool neříká, v jakém pořadí se věci dělají. Vyřízení objednávky je playbook: kontrola zákazníka, sklad, faktura, dodací list, balík, expedice. Ten patří do návodu, ne do popisu jednoho toolu.
Chtěl jsem jedno řešení pro obě potřeby: model dostane návod i správné tooly najednou, ale jen když je potřebuje.
Nápad: skill je brána k toolům
Pro agenty už mám Agent Skills (skills/eshop-<modul>/SKILL.md + references/). Skill popisuje, jak se v daném modulu pracuje. Spojil jsem ho s nabídkou toolů:
- Model vidí v system promptu katalog skillů (jen
name+description). - Dokud skill nenačte, má k dispozici jen
core_*tooly a dva meta-toolyskill_loadaskill_read_reference. skill_load("eshop-orders")vrátí tělo skillu a odemkne tooly modulu od dalšího kroku smyčky.
Je to progressive disclosure aplikovaný na tooly. Anthropic ho popisuje u skillů (obsah se načítá po úrovních) a u tool search toolu (defer_loading). Rozdíl proti tool searchi je v tom, že model nehledá tool podle dotazu. Vybere téma (skill) z malého katalogu a dostane celou sadu spolu s návodem.
Mapa skill → tooly
Standard Agent Skills má experimentální pole allowed-tools, to ale znamená předschválení toolů, ne jejich odemykání. Neříká, které tooly se mají modelu nabídnout až po načtení skillu. Závislosti skillu na toolech navrhuje teprve pole required-tools v diskusi agentskills #392. Skilly navíc sdílím s claude.ai a ChatGPT, takže do frontmatteru nepřidávám vlastní klíče. Proto je mapování jedna mapa v kódu, podle prefixu názvu toolu:
CORE_SKILL = "eshop-core"
ALWAYS_PREFIXES = ("core_",)
SKILL_TOOL_PREFIXES: dict[str, tuple[str, ...]] = {
"eshop-core": (),
# playbook „vyřídit objednávku“ prochází sklad, fakturu, dodací list i balík
"eshop-orders": ("orders_", "customers_", "store_", "invoices_", "deliverylists_", "balikobot_"),
"eshop-store": ("store_",),
"eshop-invoices": ("invoices_", "customers_"),
"eshop-deliverylists": ("deliverylists_",),
"eshop-balikobot": ("balikobot_",),
"eshop-analytics": ("analytics_",),
}
Rozhodnutí, která za tím stojí:
- Prefixy, ne seznam názvů. Nový tool
store_xyzse zpřístupní sám, když sedí na prefix. Konvence<modul>_<akce>z MCP (název toolu je veřejné rozhraní) tak slouží i jako klíč. - Skilly se mohou překrývat.
store_*odemykáeshop-storeieshop-orders. Orders je „průřezový“ skill, jehož playbook sahá do skladu, faktur, DL i balíků. Bez překryvu by model musel načítat čtyři skilly jen kvůli jednomu úkolu. eshop-coreje vždy aktivní. Je celý v system promptu (pravidla zápisů,dry_run, práce s konflikty), takže ho model nenačítá. Odpovídající toolycore_*(core_whoami,core_search, číselníky) jsou také vždy k dispozici.- Pojistka proti zapomenutí. Test ověří, že každý tool v registry spadá pod
core_nebo některý prefix a že klíče mapy odpovídají skillům. Nový prefix bez doplnění mapy shodí test.
Jak to běží ve smyčce
Smyčka je LangGraph StateGraph se dvěma uzly:
START → agent ──(tool_calls?)──► tools ──► agent …
└──(žádné / stop)──► END
Důležité je, že sada toolů se skládá znovu v každém kroku, ne jednou na začátku běhu:
# agent_node – každá iterace smyčky
model = make_model().bind_tools(tools.tool_specs(run.user, run.active_skills, run.base))
tool_specs vrátí tři skupiny: meta-tooly skillů, core_* a tooly aktivních skillů. Z nich zůstanou jen ty, které uživatel smí vidět:
def tools_for(active, ctx):
prefixes = prefixes_for(active)
return [t for t in protocol.visible_tools(ctx) if t.name.startswith(prefixes)]
Aktivace skillu probíhá v uzlu tools. skill_load vrátí kromě textu i activated=<skill>:
res = tools.execute(name, args, run.user, run.active_skills, run.base)
if res.activated and res.activated not in run.active_skills:
run.active_skills.append(res.activated)
V další iteraci agent_node zavolá tool_specs s rozšířeným seznamem a model dostane víc toolů. Odpověď skill_load obsahuje:
- tělo
SKILL.md(návod), - seznam referencí, které si model může načíst přes
skill_read_reference, - seznam právě zpřístupněných toolů, takže model ví, co od teď smí volat.
Dvě brány: skill a práva
Skill gating řeší kontext modelu. Nesmí se zaměnit s autorizací. Každé volání prochází v tools.execute třemi kontrolami:
t = registry.TOOLS.get(name)
if t is None: return _error("not_found", ...)
if not t.visible(ctx): return _error("forbidden", ...) # práva uživatele
if not skills.tool_allowed(name, active):
hint = skills.skills_providing(name)[0]
return _error("inactive", f"Tool {name} není aktivní – nejdřív načti skill {hint} (skill_load).")
visible(ctx)jsou práva (rights, scopes) živě z uživatele. Skill nikdy nepřidá oprávnění.inactiveje kontrola proti halucinaci. Model může zkusit zavolat tool, který zná z historie nebo z názvu, ale ještě nebyl aktivní. Chyba je výsledek pro model, ne výjimka a obsahuje přímý návod, co udělat (skill_loadnejspecifičtějšího skillu). Model se opraví sám v dalším kroku.- Teprve potom jde volání do
protocol.call_tool, stejnou cestou jako u externího MCP klienta. Validace, omezení přístupu uživatelů s omezenými právy,dry_runa audit (mcp_audit) jsou tedy identické s MCP.
Externí MCP klienti vidí všechny povolené tooly. Skill gating je vlastnost UI agenta, ne MCP serveru.
Menší rozhodnutí
- Katalog v system promptu je levný. Jen název a description, ostatní až na vyžádání. Kvalita
descriptionje rozhodující, protože podle ní model skill vybírá. V těch svých uvádím příklady uživatelských formulací („vyřiď objednávku 71781“, „co máme pod minimem“). skill_read_referenceneaktivuje skill. Reference (playbooky, slovníky, metriky) jsou třetí úroveň. Model je čte, až když je potřebuje. Název souboru prochází whitelistem a regexem, aby nešlo číst mimo adresář.- Enum názvů skillů ve schématu
skill_loadomezuje vymyšlené názvy. Neznámý název navíc odmítne server. - Parametr
intent. Ke každému toolu přidávám povinný parametrintent(jedna věta pro uživatele, co a proč model dělá). Před provedením ho odeberu. UI pak ukazuje komentovaný postup místo výpisu názvů toolů. - Ořez kontextu nesahá na skilly. Při omezování historie se výsledky
skill_loadaskill_read_referencenikdy neodřezávají, protože jsou v nich pravidla práce. - Cache katalogu podle mtime souborů, takže úprava
SKILL.mdse projeví bez restartu. - Neplatný skill se přeskočí s warningem. Nerozbije agenta (chybí frontmatter, duplicitní jméno).
Co to stálo a co přineslo
U úzkých skillů se nabídka modelu výrazně zmenší. Průřezový skill orders ale ušetří jen pětinu a ve spojení s analytikou odemkne celý registr:
| Aktivní skill | Toolů (včetně 11 core_*) | Tokenů na schémata | Úspora proti celému registru |
|---|---|---|---|
| žádný | 11 | 4 500 | 95 % |
| balikobot | 19 | 10 800 | 89 % |
| deliverylists | 19 | 15 000 | 84 % |
| store | 30 | 20 000 | 79 % |
| analytics | 30 | 23 200 | 76 % |
| invoices | 28 | 29 600 | 69 % |
| orders | 76 | 77 200 | 20 % |
| orders + analytics | 95 | 95 900 | 0 % |
Výhody
- Návod a tooly přicházejí společně, model dostane playbook ve chvíli, kdy ho potřebuje.
- Skill je jediná jednotka „znalosti“ pro AI agenta i externí agenty (claude.ai, ChatGPT, Claude Code).
- Pravidla jsou ověřitelná testem (pokrytí prefixů).
Náklady a slabá místa
- Jeden extra krok na úvod. Před prací v modulu je nutné zavolat
skill_load. System prompt to vyžaduje výslovně a chybainactiveto napraví, kdyby model zapomněl. - Mapa je ručně vedená. Standard skillů odemykání toolů neřeší, takže je to jediné místo, které je třeba udržovat (test ho hlídá).
- Hrubá granularita. Odemyká se celý modul, ne jednotlivý tool. Při „orders“ to znamená 65 toolů, s
core_*76. Kdyby to začalo vadit, řešením by bylo vyhledávání (tool search) uvnitř skillu. - Model musí vybrat správný skill. Špatná
description= špatný výběr. Evaly skillů hodnotí postup a výsledek úkolu, samotný výběr skillu zatím neměřím.
Jak to zapadá do okolí
Podobný problém řeší víc projektů. Spojení skillu s odemykáním toolů není nový vzor, tento článek popisuje jednu jeho konkrétní implementaci:
| Přístup | Jak se vybírají tooly | Zdroj |
|---|---|---|
Tool search tool (defer_loading) | model hledá podle dotazu (BM25/regex) | Anthropic, Introducing advanced tool use |
| Dynamic toolsets v MCP | progressive discovery nebo sémantické vyhledávání | Speakeasy, Sam Morrow |
| RAG-MCP | sémantické předvybrání toolů | arXiv 2505.03275 |
| Skills v LangChain / Deep Agents | skill načte instrukce; dokumentace popisuje rozšíření Dynamic tool registration, kdy načtení skillu zaregistruje jeho tooly | docs, fórum |
| Moje implementace | model načte skill z katalogu, skill odemkne svou sadu toolů s návodem | tento článek |
Související je také agentskills #392 (návrh pole required-tools pro závislosti skillu na toolech).
Jeden mechanismus dává modelu návod i správné tooly, a to ve chvíli, kdy je potřebuje.