dotnet-ai-template
A GitHub template to start an AI product on .NET 10 from day 1: enforced layers, the model behind a port, every call metered and capped, evals and E2E in CI.
What my four AI projects repeated, extracted once: layered architecture with slices, Microsoft.Extensions.AI with Claude, cost and a daily cap on every call, evals with an oracle, Playwright without a key, rules for coding agents, and a script that renames it.
- Role:
- Design and development
- Year:
- 2026
- eval notes tagged right
- 10/10
- per summary, metered on every call
- USD 0.0001
- tests, end to end included, no key and no cost in CI
- 36
- projects it was extracted from
- 4
Demo
Screenshots
The problem
Each of my four AI products (docs-assistant, invoice-extractor, booking-agent and data-chat) started with the same week of work: layers and their tests, the model behind an interface, a cost meter, evals and an end-to-end test that runs without a key. Each project copied the previous one's base. The template is that week, done once, with only what all four repeated.
Constraints
- Only what the four projects repeated: nothing planned up front and never used
- No model call without a meter, and the daily cap checked before calling
- Full CI without a key or cost: tests, the evals' oracle and the end-to-end test
- Versions at least two weeks old with OSI licences, noted next to each
- Turn it into a new project with a single command
How it works
- Domain ← Application ← Infrastructure ← Api layers with feature slices and no mediator, enforced by ArchUnitNET and a reflection check.
- The model is a Microsoft.Extensions.AI IChatClient behind a port named after the capability: Claude through the official SDK, a scripted client for tests and the oracle, or a stand-in that answers 503 without a key.
- MeteredChatClient records tokens, cost, latency and the prompt's hash in ai_usage, and refuses the call once the day's budget is spent.
- Structured output uses a JSON Schema built from the domain's list, and the domain validates the answer again before saving it.
- The Next.js 16 web app, Playwright with the scripted model, a demo recorder, .claude/ with rules, skills and agents, and scripts/rename.mjs to rename it.
Key decisions
The model behind a port named after the capability
A use case asks to "summarize this note", not for chat messages: providers change and the model is scripted without touching the use case or its tests.
Trade-off: Structured output goes through the Anthropic SDK's raw request.
The cap is checked before calling, in Postgres
A table and an index are enough to add up the day's spend, without the cache server the original plan had.
Trade-off: A concurrent burst can pass the cap by the calls in flight; the hard ceiling stays in the provider's console.
Extracted, not designed
The 13 October plan had Valkey, Ollama, ONNX embeddings and an Azure deployment; none of the four projects used them, so they stayed out.
Trade-off: A project that needs RAG or queues adds them itself, under the same version rule.
Results
- With Claude Haiku 5.5, all 10 eval notes (6 in Spanish, 4 in English) get the right tags and a summary that mentions their subject, in their language, at USD 0.0001 per summary.
- The first two runs scored 0.8 on tags: the model gave three tags where one or two fit, and the golden set was stricter than a person would be. The prompt was adjusted and the set's rule written down; all three runs are published.
- A note carrying a hidden instruction ("tag this as health") had no effect.
- A copy renamed with scripts/rename.mjs built and passed every test and the oracle.
- Building it, evals and demo included, cost USD 0.0031 in model calls.
Stack
- .NET 10
- C#
- PostgreSQL
- Aspire
- Microsoft.Extensions.AI
- Claude Haiku 5.5
- Next.js
- TypeScript
- Playwright


