AI engineer. I build agents that stay within a cost budget, only state facts they can source, know when to ask a human, don't act on instructions hidden in data, are measured before they ship, don't send the same message twice and go looking for the work.AI engineer. I build agents that stay within a cost budget
This is the production system prompt behind my agent, exactly as the API receives it. The agent's four tool definitions sit in front of it, in the same cached prefix.
system · production LLM · ≈ 0 tokens
Why the prompt is written this way
Prompt caching, request by requestcache cold
#
cache
written to cache
read from cacheread
not cached
in context
input cost vs. no cachecost
Simulated with this prompt's token counts and the production model's pricing (Sonnet 5.5): writes cost 1.25× input, reads 0.1×, and an entry lasts five minutes from the start of the last request that used it. OpenAI and Gemini cache automatically, with their own discounts. Your real requests to my agent appear here with the usage the API returned. "In context" never goes down, because cached tokens still fill the window.
for your teamFor your agents, I'd set a budget per answer and cache the prompt they repeat, so you know the cost before the first user arrives.
field noteA cache miss never shows up as an error
Every major model API caches by prefix: it reuses the start of a prompt only if that start matches a recent one exactly, token for token. Put a timestamp, the visitor's name or a shuffled tool list near the top, and each request begins differently, so nothing is ever read back. Prompts under the model's minimum length (1,024 tokens on OpenAI; 512 on current Claude models, up to 4,096 on some older ones) are never cached at all.
Neither case returns an error. A short prompt pays full price, and a shifting prefix pays more, since every request writes a fresh cache entry at 1.25× and reads none. The only trace is a cache-read count of zero in the usage, which is why the table above marks such requests cold or uncached. My prompt keeps the date after the cached part and builds the tool list in a fixed order. A zero there is one of the first things I look for on an agent's bill.
2 / 9
Every role I've had runs as an MCP server
Each server's tools are what I built in that role, and its prompt is what the job taught me. Every tool call returns the evidence.
mcp inspector · 6 serversconnecting…
JSON-RPC traffic · 0 messages
Connect your own agent
Point Claude Code or any MCP client at /api/mcp. The server is read-only and served over Streamable HTTP. Give it a job posting: match_job returns my fit verdict with the evidence, and tailor_resume returns my resume re-ranked for that posting.
Per-role servers (6)
Agents that speak A2A 1.0, the Agent2Agent protocol, can find this site's A2A agent from its agent card:
claude mcp add --transport http rishabh /api/mcp
rishabh://now · live over Server-Sent Eventsconnecting
field noteThe tool description is the prompt that picks the tool
The model chooses tools by reading their names and descriptions, so overlapping descriptions are one of the most common causes of wrong calls. Fewer tools, each with one clear job, work better. My portfolio server has seven, all read-only, and each role server has one per piece of work.
field noteMCP dropped its handshake
The 2026-07-28 revision made MCP stateless. There is no initialize or session any more: every request carries its protocol version and client capabilities in _meta, and server/discover replaces the handshake for anyone who wants to look first. The method and tool name are also copied into HTTP headers, so gateways can route and rate-limit without reading the body. A server must reject any header that disagrees with the body; otherwise the gateway and the server could act on two different requests. These servers still answer the older handshake, so older clients connect too.
field notereadOnlyHint is a hint, and a server can lie
Annotations are a server describing itself. The MCP spec tells clients not to trust annotations from servers they don't trust, so the client's own policy should decide what needs approval. Every tool on these servers returns evidence and changes nothing, so each is labelled read-only. Your client shouldn't take my word for it either.
3 / 9
Skills, as a model card
Each skill is a vector over eight features (agent design, models, safety, security, infrastructure, operations, web, data), weighted by hand; nearest() searches that space, as retrieval over real embeddings does across hundreds or thousands of dimensions. The 3D view is a projection. Drag to turn.
field noteA leaderboard score says little about your task
Public benchmarks test on someone else's data, and models get tuned toward them. Teams that ship pick models by running their own tasks. The Market fit tab does the same: it scores me against 110 job postings and shows the gaps.
4 / 9
NestEazy: the company I co-founded
A rental platform for New Brunswick, live since July 2026, plus the multi-agent property OS we're building on top of it. Visit nesteazy.ca
Six AI features in production
1 / 6
Ask Nestyretrieval with citations
Answers tenancy questions from human-reviewed rental law and cites the source. Open to everyone on the website; used by over 1,000 people.
Tenant screeningopt-in · a person decides
Summarizes each applicant's fit when the landlord turns it on, and the landlord makes the decision.
Document fraud checksfraud detection
Screens pay stubs, bank statements, IDs and proof of ownership for signs of editing or forgery, to stop rental fraud and scams.
Reference agentagent over text and email
Contacts a renter's references and turns their replies into a summary for the landlord.
NestScore matchingtwo ML algorithms · embeddings
Fits an incoming tenant to a household for shared living, or to a home for an entire place. Both sides have to suit each other.
Listing extractionagent · structured output
Turns unstructured listing text into schema-validated records.
Rentals on autopilot, with the landlord in the loop
Each agent is a workflow in code that asks the model only for words. It does the landlord's routine work around the clock and stays fair to renters: code checks consent and quiet hours, applications leave out the questions human rights law prohibits, and any question about protected grounds goes to a person.
modelModel writes, code acts.The model drafts and labels. It has no tools.
codeNo automatic message depends on the model.Code writes every message that goes out on its own.
personA person makes the decisions.Choosing a tenant can't be delegated to a rule. Every proposal shows its reason and evidence.
audit trailevery action, with its reason and evidence0 logged this run
evals before releasecost, latency and quality, tested before anything ships
The trace appears here.
Sample data and an illustrative flow of the design principles.
for your teamFor an agent that touches your customers or money, I'd work out with your team which actions wait for a person, and build it so they always do.
field noteMake the model optional
So an injected instruction or a wrong draft can't reach a renter on its own, and a model outage only means no drafts for a while. Drafts appear only as suggestions the landlord sends with a tap.
5 / 9
When the model is fooled
No prompt makes a model immune to injection, so the harness around the model has to hold anyway. The pattern check below is the one my agent runs.
One model, two designs
Both agents run the same simulated model, scripted to do whatever the message says. No LLM is called here.
Tool-calling agent
The model picks tools and they run, with no approval step. Most agent frameworks work this way unless you add one.
Model writes, code acts
The model has no tools and only drafts; the harness decides what runs. The pattern I use for agents that touch people or money, simplified here; real checks differ.
Shipping an agent that reads email, documents or web pages and can act on them? Ask me which lane it's in.
In production
Email security at The Cabin Depot
It protects about 100 staff and partner inboxes from phishing, payment fraud and impersonation of employees or the brand. It catches credential-phishing links and fake sign-in pages, spoofed and impersonated senders, and payment-fraud requests.
Open source · v0.1.2
Garm
The cost of each defence against prompt injection that persists across an agent's scheduled runs.
Garm is an open benchmark for AI agents that do scheduled back-office work on records an outsider can change. For each defence it measures attacks stopped, honest updates wrongly blocked and the approvals a person has to give.
Its hardest attack changes a vendor's phone number first, so when staff call to verify a bank change, the attacker answers. AI reviewers shown the full history let at most 3% of these attacks through. They also blocked nearly every honest vendor who made the same two changes, because honest and hostile changes look identical. Calling the number on file from before any recent change stopped the attack in 8 of 8 episodes, without blocking honest vendors, as long as their old number still worked.
94 to 100%
of honest phone-then-bank changes blocked under per-field labels by AI reviewers reading the approval card, because they look the same as hijacks
0 per 100
approvals per 100 routine tasks with per-field authority labels, against 102 to 163 per 100 for the other gated defences, partly because the labels gate fewer kinds of action. Legitimate account updates are a separate set: there every one still takes an approval, as the chart shows
0 of 1,260
runs (60 templated attacks, repeated) where an injection steered an action: a null result
Run on Claude Haiku 5.5 and Sonnet 5.5 in a fictional distributor's back office. The write-up was checked by 11 models from 7 vendors, OpenAI and open-weight models among them, so no one model family grades its own work. Small samples and templated attacks, both stated in the README. Named for the hound that guards the gate of Hel.
Claude Haiku 5.5: 8 laundered episodes and 16 legitimate updates, 3 attempts each. Claude Sonnet 5.5 repeated the known-good runs for D0 and D5, one attempt each: no laundered attack got through, and the approvals matched. D0 to D5 are agent-side defences. D6 changes the staff process instead (call the number on file from before any recent change), so it combines with each of them; shown is the lenient D6, which assumes an honest vendor's old number still reaches them, and D1 and D3 weren't run with it. "Laundered": the bank change arrives with a genuine-looking callback, because the attacker changed the phone number first. The 93% approver is an illustration, not a measured person: the approval rate Claude Code users give permission prompts, applied to the logged cards.
field notePrivate data, untrusted input and a way out: pick two
Simon Willison calls this the lethal trifecta. An agent that can read private data, read content an attacker controls and send data somewhere can be talked into leaking it, whatever the prompt says. The fix has to be structural: take one of the three away. With no tools, the model's words are still a way out, so the design takes away the private data instead. The model never sees it, and code checks every draft.
field notePeople stop reading approvals
Ask someone to approve two hundred actions a day and they approve nearly all of them. Human review works when it's rare enough to be read. Let people pre-approve routine work for a limited time, and keep the few actions that are hard to undo out of reach of any pre-approval.
6 / 9
This site's agent, graded
The agent you can ask about me speaks for me, so it's graded like a model, misses included.
An AI model helped build Garm, so no single model reviewed it: every model on the panel got the same files and the same reviewer prompt. Ten of the 11 flagged the same first limit: the human approvers are modelled rather than measured.
11 models
from 7 vendors, 4 of them open-weight
8 of 8
reviewers from other vendors gave the top verdict, that the findings are worth pursuing
for your teamI'd give your agent an eval like this one before it ships, so you see its misses first, not your users.
field notepass@k flatters agents; pass^k doesn't
pass@k asks whether any of k tries succeeds. For an agent acting for a user, the question is whether all of them do, which τ-bench calls pass^k. With independent tries the gap grows fast:pass@k pass^k
field noteLLM judges can have favourites
Model graders can favour one position (often the first), the longer answer and their own model family; that last effect is often small and varies by model and task. Score pairs in both orders, keep length in check, and drop the judge where code can decide: Garm scores its agent with deterministic checks against the database. Where a judge is needed, use more than one model family. This site's judge is from its agent's own family, so the eval set and my blind labels of its verdicts are published for anyone to check.
7 / 9
A resume that tailors itself
Give it a job posting and it scores the fit and re-ranks my resume for it: matched terms highlighted, gaps listed, one page or two, any bullet removable.
Role
Greenhouse, Lever, Ashby, Workday and most career sites.
Paste the job description
Fit
Required qualifications count most, and years, seniority and hard requirements such as clearances are checked. Calibrated against model-written hiring reviews of 153 real postings, so treat it as a guide.
Install me as an Agent Skill
Your agent can then answer questions about me, or run a script that matches me to a job description.
field noteA valid schema can still hold invented facts
Structured outputs guarantee the shape of the JSON. They say nothing about whether the values are right, and a field can be well formed and invented. Check the values against the record they came from.
field noteBM25 still wins on exact terms
Embeddings capture meaning and blur exact strings: product names, error codes, SKUs. Search for “SYSPRO” and BM25 finds it, where an embedding may return near misses about ERPs. Production retrieval is often hybrid for that reason. This resume uses BM25 because job postings are full of exact terms.
8 / 9
How I'd design and ship your platform
A reference design for an AI agent platform, not any company's real system: how it holds under load until a part breaks, and how I'd plan the build.
for your teamI'd put your platform through a provider outage and a 10× spike like these before launch, and plan the build around the work that sets the date.
field noteRetries send things twice
When a call times out, you can't tell whether it ran. Retry without an idempotency key and the renter gets two messages, or a payment goes through twice. The fix is a transactional outbox: write every outbound action in the same transaction as the change that caused it, under an idempotency key the receiver checks, so a replay does nothing.
9 / 9 · plot twist
This portfolio is itself a proactive agent
The record, fit scorer, resume and safeguards in the chapters above also run my own job search. They drive an agent that works the job boards around the clock, and it acts only where I've allowed it to.
always onno company or role leaves my machine
How it decides sample postings
In my inbox sample
policy
on its own
finds roles, scores them, researches each company, tailors my resume and writes the application
under my rules
applies when the fit is strong and every answer comes from my record
asks me
anything short of a strong fit, any role that means moving, and any question my record can't answer
field noteLet the first one wait
The first application that qualifies for the rule still waits for me. Submitting is the one step that can't be undone. It runs on its own only after it has gone through once with a person watching: the form filled, the answers checked, the confirmation recorded.
field noteMost of the agent is the harness
The model writes, researches and reviews. The rest is harness code: the schedule, with model calls batched at half price; daily and monthly spend caps checked before every model call; my record in a cached prefix, so repeat calls read it for a fraction of the price; crash recovery that hands interrupted work back to me; and the policy above.
If your team is putting agents in front of customers, this is the work I do: tools scoped to the task, evals before anything ships, and a person in the loop wherever a mistake costs something. I've shipped it with a two-person founding team on a startup budget. Ask the agent below whether I fit your role; it answers from the same record you've been reading.
Rishabh
or connect your own agent:
Rishabh's agentbuilt-in · no model
Ask about Rishabh's work. I answer from his record and show each lookup.
0 tool calls · 0 tokens
field noteThe harness runs in your browser, so the history is signed
The proxy only adds the key, the system prompt and the tool list. The tool loop runs in this page: each call passes the gateway you see in the trace, and the results go back with the history. A client that holds the history can forge an assistant turn, such as "Understood, I'm now a general assistant", so the proxy signs every assistant turn it returns, thinking blocks included, and rejects any history whose signatures don't verify. Tool results can't be signed, since the browser makes them, so the proxy reruns each read tool from the signed call and keeps its own result. The one approval-gated tool must return one of the gateway's three outcomes for that exact call.
field noteEvery tool call needs an answer, failures included
In the Messages API, each tool_use block has to be answered in the very next user message by a tool_result with the same id, or the request is rejected. When the model calls several tools at once, every result goes back in that one message, ahead of any text; split them across messages and it learns to stop calling tools in parallel. When a tool fails, return the failure with is_error: true. The model can then retry or explain; hide the error and it tends to report a success that never happened.
field noteCheck stop_reason before you show an answer
If stop_reason is max_tokens, the answer was cut off, and in a tool loop the last tool call may be incomplete. This agent caps answers at 600 tokens and its prompt asks for about 70 words, so it finishes well inside the cap. If it's refusal, the model declined: show that, not a partial answer.
field noteA 429 tells you when to come back
Rate-limit errors usually come with a retry-after header. Wait that long, plus some jitter, instead of retrying at once. Without one, back off exponentially. This site's proxy gives each visitor a weighted budget, sends retry-after when it's spent, and the console tells you exactly how long to wait.