Teaching an Agent Our Client Reporting Playbook with Vercel AI SDK v7 Skills
Our monthly client reports followed a 40-line prompt nobody dared touch. I moved the whole playbook into an uploaded skill using the new uploadSkill API in Vercel AI SDK v7.
Every team that runs an agent in production ends up with the same artifact: a giant system prompt that encodes how the company does things. Ours was the client reporting prompt. Forty lines describing our report structure, tone rules, how to compute month-over-month deltas, which numbers go in the executive summary. It got pasted into every call, cost tokens every time, and drifted out of date the moment someone edited a copy of it.
Vercel AI SDK v7 gives that artifact a proper home. Skills are a lightweight open format, a folder with a SKILL.md and optional scripts, and the new top-level uploadSkill API pushes the bundle to the provider once. After that, every inference call references it instead of re-sending it. I migrated our reporting playbook to see whether this is actually better or just tidier.
What a skill looks like
A skill is just files. The only required one is SKILL.md, with YAML frontmatter naming the skill and, crucially, describing when it applies:
client-report/
├── SKILL.md # instructions + metadata
├── scripts/
│ └── deltas.py # month-over-month calculations
└── assets/
└── template.md # our report skeleton
---
name: client-report
description: Use when generating a monthly performance report for a client.
---
Follow the structure in assets/template.md exactly.
Compute all period deltas with scripts/deltas.py, never in your head.
The executive summary is three sentences, no adjectives, numbers first.
The description is what the model reads to decide whether to pull the skill in. The full instructions load only when it is actually used. That progressive disclosure is the whole trick: the agent carries a one-line index of its capabilities instead of every playbook in full, every call.
The instruction telling the model to compute deltas with a script instead of "in its head" is the part I care most about. Arithmetic in a template is exactly where LLMs quietly ruin a client report.
Upload once, reference forever
uploadSkill takes the provider's skill API and the file bundle, and hands back a reference:
import { uploadSkill } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { readFileSync } from 'node:fs';
const { providerReference } = await uploadSkill({
api: anthropic.skills(),
files: [
{ path: 'client-report/SKILL.md', content: readFileSync('./SKILL.md') },
{ path: 'client-report/scripts/deltas.py', content: readFileSync('./scripts/deltas.py') },
{ path: 'client-report/assets/template.md', content: readFileSync('./assets/template.md') },
],
displayTitle: 'Client Report Playbook',
});
You run this in a deploy step, not per request. The providerReference is what you store. With Anthropic, using it means enabling the code execution tool and passing the reference into the container's skills, so the model can actually run deltas.py inside the provider-managed environment:
const result = await generateText({
model: anthropic('claude-sonnet-4-5'),
prompt: 'Generate the June report for Meridian Retail.',
tools: { code_execution: anthropic.tools.codeExecution_20250825() },
providerOptions: {
anthropic: {
container: { skills: [providerReference] },
},
},
});
The sibling API, uploadFile, does the same thing for data. I upload the month's metrics export once and pass its reference into the call, instead of shipping the same CSV bytes with every retry while I iterate on the report.
What changed in practice
The report the skill-based agent produced matched our template better than the prompt-based version, mostly because the template now travels as an actual file the model reads rather than a prose description of a file. The deltas came from the script, so they reconciled with the spreadsheet on the first try.
The workflow change is bigger than the quality change. The playbook now lives in the repo as a folder, gets reviewed in pull requests like code, and is versioned by re-uploading. When our head of delivery wants the executive summary to lead with revenue instead of traffic, that is a one-line diff in SKILL.md, not an archaeology dig through prompt strings scattered across three services.
Why this matters
There is a category of company knowledge that is not RAG material and not fine-tuning material: procedures. How we write reports, how we escalate, how we format things. It used to live in system prompts, which are the worst place to maintain anything. Skills make procedures a deployable artifact with a name, a version, and an owner.
Skills are portable across providers in principle since the format is open, though today the provider-managed execution path is Anthropic-shaped, so check support for your stack before committing. Start with whichever prompt in your codebase everyone is afraid to edit. That one is a skill.
Related reading
One-shot code execution can't build software. An autonomous coding agent needs a filesystem that survives across turns — to install dependencies, edit files, run the test suite, read the failures, and open a pull request. This builds that with a persistent E2B sandbox.
Voice agents used to mean marrying one provider's WebSocket event format. I used the new experimental realtime API in Vercel AI SDK v7 to build an order-status voice agent I can move between providers.
The moment you let an LLM run the code it writes, you've handed a stranger a shell on your server. Here's the threat model, and a step-by-step build of an isolated Docker sandbox your agent can safely execute code in.
Let's build something great.
Have a project in mind? We are an elite software and AI development studio ready to bring your ideas to production. Let's talk about your roadmap.