Latest Article

  • Seven Lessons From Building Governed AI Agents: Cost, Security and Governance Beyond the Demo

    A demo agent has to impress for ten minutes. A governed agent has to be cheap enough to run, safe enough to expose to real users, and accountable enough to survive an audit. Those are different engineering problems, and most of the interesting decisions sit in the second one.

    Over the past year I have built several agent prototypes on AWS (Strands agents on Amazon Bedrock AgentCore) for different organisations. This article collects what repeated across them. I have left out who they were for and what they did, on purpose: the lessons are about the pattern, not the client.

    A note on status. These were prototypes. Most ran locally or against mock backends and scripted stand-in models, some with real Bedrock runs, and none is a production system. So the cost points below are design levers with a measurement plan, not realised savings. The failures I describe are real observations from testing.

    1. Let the model orchestrate and let code calculate

    The most reliable pattern was the simplest: every figure, score and decision rule lives in deterministic tools, and the model chooses which tool to call and explains the result. In one prototype the narrative text was assembled by code and the model simply returned the finished document, which removes a whole category of invented numbers.

    Cost impact: deterministic work costs no tokens and gives the same answer every time. The model is paid for judgement, not arithmetic.

    Governance impact: if a number is wrong you debug code, not a prompt. A system prompt that forbids inventing tool arguments is a useful backstop, but the boundary should not depend on it.

    2. Anchor the work to something you can count

    One agent searched the open web for information. The first design searched by topic and then worked out which known record each result belonged to. The better design worked through a known list one record at a time, searching for that record, and checked every hit against it before storing anything.

    That change did three things. Cost scaled with the length of the list (a handful of searches per record) instead of the number of queries someone might think of. Precision improved, because a result that could not be tied to the record was sent to review instead of stored. And it created an honest limit: what the list does not contain can never be found.

    The general lesson is to give an open-ended agent a bounded unit of work. Anything you can count, you can budget.

    3. Meter the money before someone asks

    Every prototype had audit lines per tool call, and most could count calls, errors and latency by tool and by customer. Almost none could say what a conversation cost. That is the first question a finance or product owner asks.

    The cheap controls are straightforward: cap the agent loop at a small number of iterations, use a smaller model for simple tools, and cache repeated reads for a few minutes. A cache hit on a repeated read came back in a fraction of a millisecond in testing, though it only works when the model repeats an identical request, so treat it as best effort. Turn on a token ledger early, add real prices from the provider’s pricing page rather than guesses, and report tokens per conversation before anyone has to ask.

    4. Take identity from the token, never from the model

    The pattern I trust is that tools never accept a customer or tenant identifier as an argument. The caller’s identity and organisation come from the verified token and request context, so the model cannot supply, guess or swap them. If no organisation resolves, the tool returns an error instead of asking the user to type one.

    Two cautions came out of review:

    • Fail closed. In one design, if the configuration for verifying tokens was left at its placeholder values, the middleware logged a warning and skipped verification. A deployment with shipped placeholders verified nothing. A security check that silently turns itself off is worse than one that refuses to start.
    • Know where verification actually happens. Passing a token to an agent is not the same as checking its signature. Be explicit about which layer does the checking: a gateway authoriser, the runtime, or the toolkit.

    5. Masking is a control with edges. State them.

    Pattern-based masking is cheap and useful. It reliably catches things with a recognisable shape, such as email addresses, phone numbers and bank account numbers, and replaces them with placeholders before the model sees them. It does not catch names or street addresses, which have no fixed shape. Catching those needs a name list, a detector or a managed guardrail service.

    The discipline is in how you describe it. Never promise that personal data is masked in general. Say which types are detected and which are not, apply masking to both input and output, and keep audit logs to counts and codes, never values. And when a model says it has “noted” someone’s contact details, that statement is untrue and worth testing for.

    6. Put approvals in code, and remember a gate only exists when it is called

    Any action with side effects, such as sending, dispatching or changing records, should be held until a named person confirms. The pattern that held up stores the pending call outside the model, ties it to that user and that exact request, lets it run once, and expires it after a short time. The model cannot confirm on the user’s behalf.

    The lesson that surprised me was about the gap. In testing, a model given the exact request to send something looked the job up and asked “do you want to proceed?” in words, without calling the send tool. No hold appeared, because the gate only exists when the tool is called. A model politely asking a question is not an approval control, and nobody should present it as one.

    Two related honesty points. A review queue with no check on who can approve is a demonstration of the workflow, not of governance. And approval behaviour that has only been run against a scripted stand-in model has not really been tested.

    7. Evaluate behaviour, not just access

    Access control can be correct while the answers are wrong. In a small comparison of four models, a request for another customer’s data was answered by three of them with the caller’s own data, presented as if it belonged to the other customer. The server never crossed the boundary, and the answer was still misleading. Only a check on the wording caught it.

    Some habits that paid off:

    • Test the refusals. Include cases where a tool must not be called, and cases where the right answer is “I don’t know”.
    • Log the gaps. A “not found” answer that records the question gives the content team a list of missing documentation.
    • Do not rank models on a tiny sample. Ten questions illustrates how the evaluation works. It does not decide anything.
    • Label your gold data. Scores computed on labels you wrote yourself measure your assumptions until domain experts have reviewed them.
    • Measure whether an extra control earns its place. In one audit log, a model-based topic classifier produced a handful of blocked queries and every one was a false positive, while a simple word list caught everything real. Switching it off was the evidence-based choice.

    Decide early: where inference may run

    A regional inference profile can route requests outside the country you expect. If residency matters, decide it before real data goes anywhere near a model, confirm which models are available in the region you want, and write the decision down.

    What I would measure in a pilot

    MeasureWhy it matters
    Tokens and cost per conversation, by customerThe number finance will ask for first
    Share of requests answered without a model callShows how much the deterministic design is saving
    Cache hit rate on repeated readsWhether the cheap control works in practice
    Approval hold rate versus attempted actionsReveals gates that the model sidesteps
    Unsupported-question rate and logged content gapsDocumentation quality and trust
    Wrong-customer wording failures in evaluationA security failure that access control cannot see

    Where I would start

    1. Move every number and rule into deterministic tools, and let the model orchestrate.
    2. Give the agent a bounded unit of work you can count and budget.
    3. Take identity from verified context, and make verification fail closed.
    4. State exactly what masking catches and what it does not.
    5. Hold side-effecting actions in code, then test that the gate actually triggers on a real model.
    6. Evaluate refusals, wording and gaps, not just the happy path.