~/ondrejnovak ← všechny články

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 %.

Abstraktní ilustrace: turniketová brána odemyká malou sadu svítících modulů, zatímco zbytek zamčených modulů zůstává ve tmě

Ondřej Novák · 2. listopadu 2026 · 9 min čtení

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.

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ů:

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í:

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:

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).")

Externí MCP klienti vidí všechny povolené tooly. Skill gating je vlastnost UI agenta, ne MCP serveru.

Menší rozhodnutí

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í skillToolů (včetně 11 core_*)Tokenů na schémataÚspora proti celému registru
žádný114 50095 %
balikobot1910 80089 %
deliverylists1915 00084 %
store3020 00079 %
analytics3023 20076 %
invoices2829 60069 %
orders7677 20020 %
orders + analytics9595 9000 %

Výhody

Náklady a slabá místa

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řístupJak se vybírají toolyZdroj
Tool search tool (defer_loading)model hledá podle dotazu (BM25/regex)Anthropic, Introducing advanced tool use
Dynamic toolsets v MCPprogressive discovery nebo sémantické vyhledáváníSpeakeasy, Sam Morrow
RAG-MCPsémantické předvybrání toolůarXiv 2505.03275
Skills v LangChain / Deep Agentsskill načte instrukce; dokumentace popisuje rozšíření Dynamic tool registration, kdy načtení skillu zaregistruje jeho toolydocs, fórum
Moje implementacemodel načte skill z katalogu, skill odemkne svou sadu toolů s návodemtento č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.