> ## Documentation Index
> Fetch the complete documentation index at: https://docs.decimal.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Support Agent from Skills

> Tutorial: assemble a customer-operations agent from measured registry skills — a lean main prompt plus an installed skill bundle, each skill's evidence read from its live scorecard, wired so skill usage actually counts.

This tutorial assembles a **customer-operations agent** — it walks an enterprise customer through onboarding, gets tax arithmetic right on order totals, and writes the reply by the book when a customer reports a missed service-level commitment — **from registry skills, not a hand-written mega-prompt**.

All three skills in the bundle below are **published in the registry today**. Two carry the `community` badge — which records that a skill was *authored on the DecimalAI platform*, by whoever published it, DecimalAI included — and one carries `imported`, meaning it was synced from a public GitHub repo. All three were benchmarked by the hosted runner rather than self-reported, but only two of those runs produce a *lift* number: the third's headline run is a single case, so the platform withholds the lift as underpowered — and so does this page. Every number below is quoted with its case count and benchmark model so you can weigh the evidence yourself rather than take a badge as a recommendation. The job mix isn't arbitrary: it's where the registry's measured customer-facing skills actually are right now, so the tutorial can be honest about every number it shows.

If you haven't read [Assemble an agent from skills](/guides/agents-from-skills), that guide is the general version of the mental model used here.

## The mental model: agent = lean main prompt + skill bundle

An agent built this way is two things, and the skill router is the glue:

1. **A lean main prompt** — the *always-on* core: identity, mission, authority and limits, tools, one line of baseline tone, and "lean on your installed skills."
2. **An installed skill bundle** — the *on-demand* library. Each turn, the [Skill Router](/api-reference/skills/router) matches the incoming message against the bundle and surfaces the relevant skills; the agent applies them and replies.

<Note>
  **The dividing line — the one real design decision:** if something is true on *every* turn, it belongs in the main prompt. If it's true only in *some* situations, it's a skill.
</Note>

Why bother splitting? Three reasons, each measurable:

* **Context stays lean.** The onboarding phases load only when a customer is onboarding; the bracket math loads only when an amount is on the table. A mega-prompt pays for everything on every turn.
* **Each part is proven separately.** Every skill in the bundle can carry its own [ablation](/guides/authoring-skills#lifecycle-ablation-and-retirement) — with-vs-without evidence that it lifts, or evidence that it doesn't. A paragraph buried in a mega-prompt is never tested on its own, so you never find out either way.
* **Parts are swappable.** Fork one policy skill and the rest of the agent is untouched — no prompt surgery, no re-testing everything at once.

**Before** (the mega-prompt): one 900-line system prompt embedding the onboarding phase gates, the tax-bracket arithmetic, and the SLA-breach reply rules. Every turn pays for all of it, and no single rule has ever been measured.

**After** (the split): a \~15-line main prompt; the onboarding phases live in `gtm-enterprise-onboarding`, the bracket arithmetic in `progressive-tax-bracket-math`, the SLA-breach reply rules in `sla-breach-response` — each loaded when relevant, and each with its own benchmark you can read before you trust it.

### How skills reach the agent at runtime — and what counts as "used"

On every routed turn the router surfaces a **menu of name + description rows** for the matching skills (budgeted, so a growing bundle can't bloat the prompt), and the SDK injects that fragment into your system prompt. Full instruction bodies load on demand — this is [progressive disclosure](/guides/agents-from-skills#progressive-disclosure-descriptions-always-bodies-on-demand).

That menu row is only the *first rung* of a three-rung ladder the platform tracks per skill, per turn:

| Rung          | Meaning                                                   | What records it                                                                                                                |
| ------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **Offered**   | The skill's name + description row was shown to the model | The router menu / `smart_route` fragment                                                                                       |
| **Delivered** | The skill's *full body* reached the model                 | A `load_skill` tool serve, budgeted body injection (`inject_skill_body=True`), or a disk install/export the runtime loads from |
| **Activated** | The skill counts as *used* on this trace                  | A `load_skill` serve, an explicit `log_skill_activation`, or a server-side grade                                               |

<Warning>
  **Menu-only injection is offered, not used.** If your integration stops at splicing the routed `prompt_fragment` into the system prompt, the platform can only ever record the *offered* rung — no body was delivered, and nothing was activated. Your agent may well be following the one-line description, but there is no honest evidence of that, so per-skill effectiveness panels will show offered counts with no activation signal. Don't stop there: wire one of the counting paths in [Wire it up so usage counts](#wire-it-up-so-usage-counts) below.
</Warning>

<Note>
  **Honest status:** on-demand body loading via the `load_skill(name)` tool is rolling out **per adapter** — it's live on the `openai_agents` and `pydantic_ai` adapters, where the model calls the tool and the body arrives as a tool result. Adapters without a tool loop (`anthropic`, `langchain`) can opt into budgeted body injection with `inject_skill_body=True` (off by default). See [the router reference](/api-reference/skills/router) for the current mechanics and budgets.
</Note>

## The customer-operations bundle (skill map)

Decompose the role into jobs-to-be-done, then map each job to a skill. Every skill below is published in the registry today — the numbers are the registry's, quoted with their evidence (re-read from the live API on 2026-08-17; each skill's scorecard is the canonical copy, and skills get re-benchmarked, so expect drift):

| Turn stage | Job                                             | Skill                          | Verified lift                  | Evidence                                                              |
| ---------- | ----------------------------------------------- | ------------------------------ | ------------------------------ | --------------------------------------------------------------------- |
| Onboard    | Run the enterprise onboarding phases in order   | `gtm-enterprise-onboarding`    | +78.3 pts (0% → 78.3% pass)    | 18/23 cases, LLM-judged, `gemini-3.6-flash`                           |
| Decide     | Marginal tax-bracket arithmetic on order totals | `progressive-tax-bracket-math` | **None published**             | Headline run is 1 case, code-graded — lift withheld as *underpowered* |
| Respond    | Answer an SLA-breach complaint by the book      | `sla-breach-response`          | +77.3 pts (13.6% → 90.9% pass) | 20/22 cases, LLM-judged, `gemini-3.6-flash`                           |

<Warning>
  **This bundle is smaller than a real support agent needs, on purpose.** Earlier revisions of this
  page listed five skills; three of them have since been unpublished from the registry, and a fourth
  (`currency-minor-units-from-reference`) no longer earns a place: its headline run is **2 cases**
  with lift withheld as *underpowered*, and the one adequately-powered run on its card — 22 cases on
  `gemini-3.6-flash`, which the registry does **not** headline — came back **+4.5 pts, 1 of 22 cases
  passing**. `progressive-tax-bracket-math` is still in the table but its headline run is
  now a single case, so it shows no lift at all. Rather than quote numbers that no longer hold, the
  table above says exactly what each skill's scorecard says today. Treat it as a
  worked example of the *method*, not a bundle to ship: run the same check against the registry for
  the jobs your own agent actually does.
</Warning>

Four honesty notes before you install anything:

* **Lift is model-relative.** Both published runs were measured on `gemini-3.6-flash`. A stronger base model may need the skill less; a weaker one more. The number tells you the skill supplies knowledge *that model* lacked, not a universal constant.
* **Read the two pass rates, not just the gap between them.** Both measured skills lift about 78 points — but from baselines of 0% and 13.6%, meaning the base model could barely do either job unaided. That is the easy case to read. The hard case is a skill that lifts 6 points over an 85% baseline: same "it works", very different purchase, and only the pair of pass rates tells you which one you're looking at. A headline delta on its own never does.
* **One of the three has no lift number, and that's the honest state.** `progressive-tax-bracket-math`'s headline benchmark run is a *single* code-graded case, so the platform withholds lift as **underpowered** and its card shows no delta. A *later* 22-case run on `gemini-3.6-flash` (2026-07-30) did show +80.0 pts (10% → 90%), and you can still see it in the per-model panel on the skill's page — but the registry headlines the older single-case run (2026-06-26) instead, and this page won't promote a non-headline number to a headline claim. Treat the skill as *plausible and unproven*: the arithmetic procedure is short and checkable, and if you want it in a real bundle, re-run its eval suite on your own cases and read your own number.
* **A badge tells you where a skill came from, not that anyone vouches for it.** `community` means the skill was authored on the DecimalAI platform — by an outside publisher or by DecimalAI, which authors and publishes skills into its own registry like everyone else. `imported` means it was synced from a public GitHub repo. Neither is an endorsement. There *is* a third badge — **★ Featured**, with its own tab in browse — and the honest reading of it is "recent and adopted", not "measured": it is computed, not curated. A weekly job promotes any skill whose [SkillScore](/guides/skillscore) clears 0.65, whose install count is over 20, and which was **created in the last 90 days**, and demotes it back to `community` the moment it stops qualifying — so no human awards it, and no skill keeps it by being good three months ago. Two of those three inputs are adoption and age, and the third need not rest on a benchmark at all: SkillScore blends live-usage, demand and benchmark legs, and a skill can score a full 1.00 with its benchmark leg empty. That is not hypothetical — of the three skills carrying ★ Featured on 2026-08-17, one publishes **no lift number at all** (its run is withheld as *not comparable*). The one genuinely curated tier, `verified`, was retired in July 2026; it matches zero skills and nothing is promoted to it. What the registry gives you instead of an endorsement is the measurement: verified runs from the hosted runner feed the scorecards; self-reported runs never do.

**The gaps are yours to author.** This bundle onboards, computes, and communicates — but it doesn't enforce *your* reply structure, a de-escalation register for upset customers, or your escalation thresholds. The registry doesn't currently have lift-measured skills for those jobs, and this tutorial won't pretend it does. They're classic `preference` skills, and writing them is a one-guide job: follow [Authoring Skills](/guides/authoring-skills), prove each lifts, and slot them into the bundle. Resist the urge to stuff them into the main prompt instead — "the customer is upset" is a *situation*, and situational knowledge is a skill.

## The process, in six steps

<Steps>
  <Step title="Decompose the role into jobs-to-be-done">
    The turn stages above: onboard, decide, respond, escalate. Write them down before touching any skill — the decomposition is what keeps the bundle honest. If a job doesn't appear in real conversations, it doesn't need a skill.
  </Step>

  <Step title="Map each job to a measured skill">
    Reuse from the [registry](/guides/registry) first — filter for verified benchmark runs and read the case counts, not just the headline delta. Author the gaps with the [skill-authoring guidance](/guides/authoring-skills). Classify each as you go (capability vs. preference, public vs. private) — the classification tells you which ones you'll customize later.

    <Tip>
      This is the step you repeat once per job, so it's the one worth automating. Add the registry to
      your MCP client once:

      ```bash theme={null}
      claude mcp add decimalai -- uvx decimalai-mcp
      ```

      and work the whole list in one conversation instead of one browser tab per job:

      ```text theme={null}
      For each of these jobs, find the best-measured registry skill and give me its
      verified lift, both pass rates, case count, and its safety band:
      1) run enterprise onboarding phases in order
      2) marginal tax-bracket arithmetic on order totals
      3) write the reply when a customer reports a missed SLA commitment
      ```

      The tools are read-only, which is the right shape here — you want to *compare evidence* across
      candidates before anything lands on disk. See [MCP server](/guides/use-skills-without-the-sdk#mcp-server).
    </Tip>
  </Step>

  <Step title="Prove each skill lifts — ablation is the per-skill gate">
    Run each skill's [eval suite](/guides/skillevaluation) with and without the skill loaded. A skill that doesn't lift doesn't belong in the bundle — it costs tokens on every activation and buys nothing. Registry skills usually arrive with a verified run attached, but check that the run is powered enough to mean anything — a one-case run tells you nothing, which is why the platform withholds a lift number for it. Skills you author need a run before you trust them, and any skill whose evidence is thin — a handful of cases, or a lift measured on a model unlike yours — deserves a re-run on your own cases.
  </Step>

  <Step title="Assemble: install the bundle, write the lean main prompt">
    Fork each registry skill into your org and [assign the bundle to your agent](/guides/skills#agent-skill-assignment). Then write the main prompt (below). There is no orchestration code — the router *is* the orchestration.
  </Step>

  <Step title="Wire up a counting activation path — and verify it counted">
    Menu injection alone records *offered* and nothing else. Enable the skill loader (or log activations explicitly) and run one test ticket end-to-end before trusting any dashboard number — the concrete checkpoint is in [Wire it up so usage counts](#wire-it-up-so-usage-counts).
  </Step>

  <Step title="Evaluate end-to-end, then fork-to-private">
    Per-skill ablation proves each part; a small trajectory eval proves the assembled agent (section below). Then replace the template policies in your forks with your real rules — and re-run the benchmarks, because your rules are different knowledge.
  </Step>
</Steps>

## Wire it up so usage counts

**Setup first.** Both paths below need the SDK with the matching framework extra, and an API key in the environment — `decimalai.init()` reads `DECIMAL_API_KEY`:

```bash theme={null}
pip install "decimalai[openai-agents]"   # Path A (OpenAI Agents)
pip install "decimalai[langchain]"       # Path B (LangChain)
export DECIMAL_API_KEY="dai_sk_..."      # Settings → API keys: app.decimal.ai/settings/api-keys
```

Installing the bundle gets skills *offered*. To get honest per-skill usage evidence, pick the counting path your stack supports:

**Path A — the skill loader (OpenAI Agents / Pydantic AI).** One flag. The router injects the menu, registers a `load_skill(name)` tool, and every body the model pulls is recorded as delivered *and* activated — the model asking for the skill is the activation signal:

```python theme={null}
import decimalai
from decimalai.openai_agents import instrument

decimalai.init()  # reads DECIMAL_API_KEY from env
instrument(agent_name="customer-operations-agent", enable_skill_loader=True)

# The model reads the menu and calls load_skill("gtm-enterprise-onboarding")
# on its own; the body arrives as a tool result mid-turn.
```

<Note>
  **The adapter entry point was called `install()` in 0.10.0 and earlier.** It was renamed to **`instrument()`** in **0.10.2** — same arguments, same behaviour — because `install` had come to mean something else entirely: adding a **skill** to your workspace. `install()` still works and emits a `DeprecationWarning`. See [Vocabulary](/guides/vocabulary).

  `SkillRouter.install()` elsewhere on this page is that other meaning, and keeps its name.
</Note>

**Path B — body injection + explicit logging (LangChain / Anthropic).** These adapters have no tool loop, so opt into budgeted body injection (records *delivered*), and log activation explicitly where your code applies a skill:

```python theme={null}
import decimalai
from decimalai.langchain import instrument

decimalai.init(inject_skill_body=True)  # bodies injected, trimmed + budgeted
instrument(agent_name="customer-operations-agent", enable_skill_loader=True)

@decimalai.trace(agent_name="customer-operations-agent")
def handle(ticket: str) -> str:
    reply = agent.invoke({"input": ticket})
    # When the skill's rules were actually applied on this turn:
    decimalai.log_skill_activation(name="gtm-enterprise-onboarding")
    return reply["output"]
```

`log_skill_activation` attaches to the **active trace**, so it must run inside a `@decimalai.trace()`-decorated function or a `start_trace()` block — called with no trace open it raises `DecimalConfigError: No active trace`. It's deliberately manual — your claim that the skill was used, attached to the trace, and the portable fallback on any integration including the [generic SDK](/api-reference/skills/router).

<Note>
  One flag name, two mechanisms: on `openai_agents` / `pydantic_ai`, `enable_skill_loader=True` registers the `load_skill(name)` **tool** (Path A); on `langchain`, which has no tool loop, the same flag injects the routed skill menu into the prompt instead — bodies only arrive there via `inject_skill_body=True` (Path B).
</Note>

### Verify it counted

Run one realistic message through the agent ("We're a new enterprise account — what happens in the first two weeks, and what's the tax on a \$48,000 order?"), then check three places:

<Check>
  **Checkpoint — you should now see:** (1) in your run log, a `load_skill("gtm-enterprise-onboarding")` tool call with the body as its result (Path A) or your explicit `log_skill_activation` line executing (Path B); (2) in the dashboard, the new trace listing the skill under its active skills; (3) on the skill's detail page, the activation count ticking up shortly after.
</Check>

If the trace shows routing decisions but no active skills, you're on the offered-only path — the fragment was injected but no body was ever delivered and nothing was activated. That's the expected result of menu-only wiring, not a bug in your agent; enable one of the paths above and re-run. Don't read offered counts as usage evidence, and don't expect anyone else to.

## The main prompt, concretely

Everything here is *always-on*; everything situational lives in a skill:

```text theme={null}
You are the customer-operations agent for <product>. Your job is to move accounts
through onboarding accurately and kindly, get order arithmetic right, and escalate
anything outside your authority.

You may: run the onboarding phases, look up orders, compute order totals, draft
customer comms, and escalate.
You may NOT: promise refunds, credits, or exceptions beyond what your policies
grant, or make commitments on price or roadmap.

Tools: order_lookup, account_lookup, create_ticket, escalate_to_human.

Lean on your installed skills — they carry the onboarding phase gates, the
tax-bracket arithmetic, and the SLA-breach reply rules. When a request arrives, load
the skill surfaced for that step rather than improvising.

Baseline tone: warm, concise, specific.
```

Apply the dividing line to every candidate sentence:

**Before** (situational knowledge leaking into the main prompt): "Enterprise onboarding runs in four phases and phase 2 gates on the security review; marginal brackets apply per band, not to the whole total; SLA credits come from the agreement, never invented…" — paid on every turn, tested never.

**After**: the onboarding phases live in `gtm-enterprise-onboarding`, the bracket arithmetic in `progressive-tax-bracket-math`, the SLA-breach reply rules in `sla-breach-response` — each loaded when relevant. The main prompt keeps only the always-true boundary: "You may not promise refunds beyond what your policies grant."

<Tip>
  The "you may NOT" block earns its always-on cost: authority limits must hold on every turn, including turns where no policy skill fires. Limits go in the main prompt; the rules that implement them go in skills.
</Tip>

## The end-to-end eval: the agent-level ablation

Per-skill benchmarks are unit tests. The assembled agent needs an integration test: a **trajectory eval** over realistic tickets, grading the whole run —

* Did it run the onboarding phases in order, and respect the gates between them?
* Is the computed tax right — marginal brackets applied per band, not to the whole total?
* Did the SLA-breach reply concede the confirmed miss and stick to the remedy the agreement defines — no invented credit figures, no never-again promises?
* Did it escalate exactly when warranted — and *not* when it wasn't?

Fifteen to twenty messages spanning those axes is enough to start. Include messages that exercise skill *handoffs* (a mid-onboarding SLA complaint that also needs a revised quote touches all three skills), and re-run the set whenever you swap a skill in or out — it's the same with-vs-without discipline as [skills ablation](/guides/authoring-skills#lifecycle-ablation-and-retirement), applied one level up.

<Note>
  **Honest limits:** there is no one-button trajectory eval on the platform today. This is a practice you compose from existing pieces — run the tickets through your agent, grade the trajectories with your own harness or [trace evaluators](/guides/evaluations), and keep the ticket set in version control next to the agent. Per-skill benchmarks and live [skill observability](/tutorials/skills-observability) remain the platform-measured layers underneath.
</Note>

## Fork-to-private: making it yours

The bundle above runs on registry skills whose policies are **worked examples, not your rules** — `gtm-enterprise-onboarding`, for instance, carries one published four-phase onboarding framework, applied strictly. That's exactly what makes it benchmarkable, and exactly why you shouldn't ship it as-is.

To productionize, [fork](/guides/registry#2-fork-into-your-org) each skill and replace the example rules with your real ones — your onboarding gates, your escalation thresholds, your SLA remedy rules. Keep each skill's eval-suite shape, swap the expected outputs for yours, and **re-run the benchmark: your rules are different knowledge, so the registry's lift number no longer applies to your fork.** Reference-style skills (`progressive-tax-bracket-math`) usually carry over unchanged — the arithmetic is the arithmetic — but check the regression column before relying on them.

Your forks are private to your org unless you publish them — see [visibility and forking](/guides/registry) for the contract.

## Other archetypes, same recipe

The measured registry is strongest today in back-office and developer work — data extraction and normalization, finance and reference lookups, commit and code conventions — which is why this tutorial's bundle is thin and leans on onboarding, arithmetic, and comms rather than a full support desk. The recipe transfers to any role the registry (or your own authoring) can cover with *measured* skills:

* **Document & back-office ops** — extraction schemas, canonicalization, reference-grounded lookups.
* **Coding** — commit conventions, code style, review checklists.
* **Data analysis** — SQL conventions, computed analysis with order-of-operations discipline.

Decompose the role, map the jobs to measured skills, prove the lift, assemble, wire a counting activation path, evaluate end-to-end, fork the policies. The archetype changes; the recipe doesn't — and neither does the rule that a skill without with-vs-without evidence is just prose.

## Related

<CardGroup cols={2}>
  <Card title="Assemble an agent from skills" icon="layer-group" href="/guides/agents-from-skills">
    The general guide: the mental model, router mechanics, progressive disclosure, and choosing skills from the registry.
  </Card>

  <Card title="Authoring Skills" icon="pen-ruler" href="/guides/authoring-skills">
    Write the gap skills — classification, description craft, body craft, and the honest eval suite.
  </Card>

  <Card title="Community Registry" icon="store" href="/guides/registry">
    Discover, fork, receive upstream updates, publish your own.
  </Card>

  <Card title="skillevaluation" icon="flask" href="/guides/skillevaluation">
    The open A/B benchmark that produces the lift numbers in this tutorial — and withholds one when the evidence is too thin.
  </Card>
</CardGroup>
