Your Team's AI Doesn't Need Another Mega-Prompt. It Needs Skills
Your junior opens Cursor (or Claude Code, or Copilot Chat: you pick your poison) and asks for a new FastAPI endpoint. The agent invents a response model shape you’ve never used, skips dependency injection, and puts business logic in the route. It writes tests with unittest.mock because that’s what the training data liked in 2025.
Your senior opens the same repo, pastes a 2,000-token “team standards” blob into the chat, and gets something closer to your house rules: until next week, when they forget to paste it and the agent invents conventions again.
Same codebase. Same house rules in someone’s head.
Stop lengthening the system prompt.
Write a skill: a small, triggered instruction file that sits between repo-wide guidance and one-off chat: so the agent loads the right workflow when the task matches, not when someone remembers to paste a whole manifesto.
Same instinct as contracts for architecture review: shape the work before you trust the prose.
The Missing Layer
You’ve probably already done the responsible thing: an AGENTS.md (or CLAUDE.md, or a .cursorrules file that grew teeth) that tells every agent the always-true facts: uv, Python version, where apps live, don’t commit .env, etc.
Then the workflows pile in. How you add a FastAPI endpoint, how you write an Alembic migration, how you review a PR: all of that lands in the same blob, or it never leaves Slack.
When task workflows live in the repo-wide file, two things happen every time:
- The agent gets a wall of text that competes with the actual task for context.
- Half the wall is irrelevant to this request, so the model half-ignores it: including the paragraph you cared about.
Chat-only reminders fail the other way: they only work when someone remembers to type them.
Skills are the missing layer: a focused playbook the agent loads when the task matches a trigger. Adding an endpoint, changing a Django model, writing a commit in your format: those are triggers.
Same idea whether your tool calls them Skills, discoverable custom instructions, or slash-commands backed by markdown. The pattern matters more than the the actual vendor folder name.
Three Buckets: AGENTS.md vs Skill vs One-Off Prompt
Wrong bucket is how you get a mega-prompt nobody trusts. Before you write another markdown file, park the content in one of three places:
| Bucket | What goes here | Example |
|---|---|---|
Repo-wide (AGENTS.md / equivalent) |
Always-true project facts | Package manager is uv; Python 3.12; apps live under src/; never commit .env; PR title format |
| Skill | A repeatable workflow with a clear trigger | “Add a FastAPI endpoint”: router shape, Pydantic models, DI, pytest layout, what not to invent |
| One-off prompt | This conversation only | “Rename this field and update callers in these three files” |
Decision rules:
- Every request in this repo should know it → repo-wide.
- Only this class of task should know it → skill.
- You’ll never need it again → chat. Don’t fossilize it.
Here is a concrete example: “we use httpx, not requests” is repo-wide. The checklist for adding an endpoint with your fixtures is a skill. “Fix the flaky test in test_billing.py” stays in chat.
If a skill starts sounding like a second AGENTS.md, split it. If AGENTS.md starts sounding like five skills glued together, extract the skills.
Anatomy of a Python Skill (Structure + Content)
A useful skill file has five parts. Miss one and you get more the equivalent of a prompt than a focused skill.
1. Name
Short, verb-ish, scoped. Prefer fastapi-endpoint over python-helpers or team-standards. Humans find it by name; tools index it by name. If it could cover half your engineering org, it’s too wide: split before you write the body.
2. Description / trigger
If this paragraph is vague, the skill never loads, or it loads for the wrong tasks and burns context. Many agents decide from the description alone. Write what it does and when to use it, in third person, with the words your teammates actually type:
Adds FastAPI route handlers with Pydantic request/response models,
dependency-injected services, and pytest coverage matching this repo's
patterns. Use when the user asks to add or change an API endpoint,
router, path operation, or HTTP handler in this FastAPI project.
“Helps with Python” is not a trigger. “Endpoint”, “router”, “path operation” are.
3. Instructions
Assume the model already knows Python, FastAPI, and pytest. Do not re-teach HTTP. Teach instead your deltas:
- Where routers live and how they’re included
- How you name schemas (
FooCreate/FooReadvs inventingFooSchema): same discipline as not letting flexible payloads invent your types - Your
Dependsstack, not a generic DI tutorial - Error shape you standardized (handlers, problem+json, whatever shipped)
- Test layout and fixtures that already exist
- Commands you actually run (
uv run pytest)
The more fragile the task, the tighter the skill. Code review can say principles; migrations and public API shapes need checklists or templates.
4. Optional references and scripts
Keep the main file short. Long tables, full OpenAPI examples, fragile shell sequences: put those in linked files or scripts the agent opens only when needed. An 800-line skill that crowds out the user’s request is noisy, not thorough.
For Python teams, scripts earn their keep when consistency beats prose: a small validator, a codegen stub, a checklist runner. Prefer “run this script” over “carefully reproduce these 40 lines of boilerplate.”
5. Success criteria and anti-patterns
Say what done looks like and what to refuse:
## Done when
- Route registered on the existing router (no new app instance)
- Request/response models in the schemas module used by this domain
- Service logic not inlined in the path function
- At least one happy-path and one validation-failure test
- `uv run pytest` passes for the touched tests
## Do not
- Introduce a new ORM or HTTP client
- Add a global singleton "app state" for DB sessions
- Invent a parallel error response shape
- Skip auth dependencies that sibling routes use
Anti-patterns are how you stop the agent from “improving” your stack. Without them, helpfulness becomes entropy: the same failure mode as an unconstrained agent with a goal and too much rope.
What belongs vs what doesn’t
In: layout conventions for that workflow; preferred libraries when choice exists; output templates (commits, PRs, ADRs); commands to run; explicit non-goals.
Out: teaches the model what it already knows; secrets and tokens; whole style guides that belong in repo-wide files or linters; “be careful and think step by step” filler.
If Ruff or mypy can enforce it, prefer the tool. Skills are for judgment and structure the linter can’t see, not for a second pyproject.toml.
When the workflow is an agent product (tools, retries, typed outputs) rather than an editor playbook, you want a framework contract too: see PydanticAI vs LangChain and structuring LLM outputs with Pydantic.
An Example: FastAPI Endpoint Skill
Here’s a v1 you can paste and rename to your tree. The structure is the point; the paths are placeholders.
Directory shape (tool-agnostic: your editor may use .cursor/skills/, .claude/skills/, or another convention):
fastapi-endpoint/
SKILL.md
examples.md # optional: one golden endpoint + test
SKILL.md skeleton:
---
name: fastapi-endpoint
description: >-
Adds FastAPI path operations with Pydantic models, dependency injection,
and pytest coverage matching this repository. Use when adding or changing
an API endpoint, router, or HTTP handler.
---
# FastAPI endpoint
## Before you change code
- Read the existing router module for this domain; extend it — don't create a parallel app.
- Reuse schemas and dependencies already imported by sibling routes.
- Prefer `uv run pytest` for verification.
## Implementation checklist
1. Pydantic models: `*Create` / `*Update` / `*Read` in the domain schemas module.
2. Service function for business logic; path operation stays thin.
3. Path operation uses the same auth/DB dependencies as sibling routes.
4. Register route on the existing `APIRouter`; confirm include in the app factory.
5. Errors go through existing handlers — no ad-hoc `{"error": "..."}` dicts.
## Tests
- Happy path: status code + response body shape.
- Validation failure: 422 (or project equivalent) with invalid payload.
- Auth failure if the route is protected.
- Put tests next to existing API tests; reuse fixtures — don't invent a new client fixture if one exists.
## Done when
- [ ] Checklist above complete
- [ ] Touched tests pass under `uv run pytest`
- [ ] No new dependency added without explicit user approval
## Do not
- Inline DB queries in the route if sibling routes use a service layer
- Add `requests` / new HTTP clients
- Create a new `FastAPI()` instance in a feature module
That’s enough for a v1. Add examples.md only when the team keeps arguing about shape: one golden endpoint, one golden test, and an instruction to match that structure.
Same five parts for a Django skill2, with different trigger words and checklist nouns. A description might read:
Adds or changes Django models with migrations, admin registration, and
tests matching this repository. Use when the user asks to add a model,
change fields, create a migration, or update ModelAdmin.
Checklist deltas become: model in the right app, migration generated and reviewed, admin registered if the team does that, tests next to existing model tests. Don’t merge FastAPI and Django into one skill unless the triggers are identical. Which they aren’t.
Failure Modes
You’ve seen a good skill. Here’s how they start to fail:
Too long. Every token competes with the user’s request and every other loaded skill. If you need a short novel, use progressive disclosure (short main file, details on demand). If everything is “critical”, then nothing is.
No trigger. A sharp body with a vague description is a file that never loads. Put the words your teammates type in the description: “endpoint”, “migration”, “celery task”, “fixture”.
Prompt in disguise. Inspiration with no checklist, no template, no “done when”. Agents do better with shaped output than with suggestions.
Contradicts repo-wide guidance. Skill says Poetry; AGENTS.md says uv. Under pressure the agent picks at random. Always-true facts have one home; skills only specialize.
Kitchen-sink. python-everything that covers linting, endpoints, Django admin, and release notes. Narrow skills compose the domains. Fat skills fail, split by trigger.
Unowned. Nobody reviews it. Six months later it documents a stack you migrated off. A skill without an owner is also tech debt.
Roll Out Without Drowning the Team
Don’t announce a “skills platform”. Ship two or three skills that match the tasks people already ask the agent to do on a regular basis.
A good starter set for a FastAPI/Django team:
- Endpoint / view change (the example above)
- Data model change (migrations, constraints, what not to rewrite)
- PR / review helper (your checklist, not a generic “be thorough”)
Commit team skills in the repo under your tool’s project path (for example .cursor/skills/ or .claude/skills/) so a clone picks them up with the code. Personal skill directories are for individual preferences, like commit voice, local tooling, not house architecture.
Rules of engagement:
- PR the skill like a runbook. Same review bar as CI config. If a senior wouldn’t trust it in an incident, it doesn’t merge.
- Verify it loads. After merge, have a second engineer run the trigger task cold (no pasted manifesto). In Cursor (or Claude), ask for a new endpoint and check that the agent pulled in
fastapi-endpoint(skill mentioned in the chat, or visible in the skills/context UI) before it starts inventing files. If your tool uses slash-commands instead, run/fastapi-endpoint(or your name) once on purpose, then once by describing the task without the slash: you want discovery and the explicit path to work. No citation and a freestyleFooSchemain the route file means the skill never loaded; fix the description or the install path before you call the rollout done. - Document the buckets in
AGENTS.mdin five lines: always-true facts live here; workflows live in skills X/Y/Z; don’t paste mega-prompts. - One owner per skill. A name in the PR description is enough at the start.
Resist skill-ifying every wiki page. Unused skills are noise in discovery and context.
Review and Kill Criteria
Review a teammate’s skill the way you’d review a runbook:
- Trigger: Would this load for the right requests and stay obscured otherwise?
- Delta-only: Does it teach our conventions, or re-teach the framework?
- Conflict check: Any contradiction with
AGENTS.mdor linters? - Done when: Can a human tell if the agent finished the job?
- Length: Can you cut 30% without losing behavior?
Kill or rewrite when:
- Nobody has invoked it in a month and nobody can name when they would
- It documents a stack you no longer use
- Agents follow it and still produce PRs that always need the same human fix: the skill is failing or incomplete
- Two skills fight for the same trigger
Skills are code-adjacent process. If they become a dead process then that is even worse than no process, because it teaches the wrong default with confidence.
What To Do Next
- Open your
AGENTS.md(or equivalent). Move any task-specific runbook into a draft skill; leave always-true facts. - Write one FastAPI (or Django) skill with a sharp description, a checklist, done-when, and do-not.
- PR it. Have a second engineer ask the agent to perform the workflow without pasting extra context.
- Compare that PR to what you get from chat-only today. Keep what worked; cut what the agent ignored.
You’re not trying to make the model smarter. You’re trying to make your team’s defaults loadable: so the junior, the senior, and the agent are finally building the same components.
If you later expose those workflows as tools the agent can call (not only markdown it should read), Building an MCP Server for Dokku with FastMCP is a good read about an adjacent pattern: skills shape how the editor helps; MCP shapes what the agent can invoke.
Follow me on Twitter: https://twitter.com/DevAsService
Follow me on Instagram: https://www.instagram.com/devasservice/
Follow me on TikTok: https://www.tiktok.com/@devasservice
Follow me on YouTube: https://www.youtube.com/@DevAsService
Comments ()