Agentic Engineering: Building Agents You Can Trust
The agent loop, tool design, context engineering, guardrails and evals for building AI agents safely.
Agentic engineering is the practice of building software where a language model decides what to do next, calls tools to act, and loops until a goal is met, while you design the guardrails that keep it useful and safe. This guide explains the building blocks, the patterns that work, and how to add an agent to a real product without losing control of it.
Workflow, assistant or agent?
Not every AI feature needs an agent. Pick the simplest thing that works.
| Type | Who decides the steps | Example | Risk |
|---|---|---|---|
| Single call | You | Summarise a review | Low |
| Workflow | Your code, with model calls at fixed steps | Extract fields, validate, then save | Low to medium |
| Agent | The model, at run time | "Find why checkout fails and fix it" | Higher, needs guardrails |
Start with a single call. Move to a workflow when steps are known. Use an agent only when the path genuinely cannot be fixed in advance.
The agent loop
Everything else in agentic engineering is about making this loop reliable: which tools exist, what the model sees, when it must stop, and who approves risky steps.
The building blocks
- Model: reasons and chooses the next action. Use a stronger model for planning and a smaller, faster one for simple steps.
- Tools: functions the model can call, each with a name, a description and a typed input schema. Tools are the agent's hands, so design them carefully.
- Context: instructions, retrieved knowledge, conversation history and tool results. This is the model's only working memory.
- Guardrails: permissions, limits, validation and human approval.
- Evaluation: tests that tell you whether the agent actually works.
Patterns worth knowing
| Pattern | Idea | Use when |
|---|---|---|
| Prompt chaining | Break a task into fixed steps, each a model call | Steps are known, accuracy matters more than speed |
| Routing | Classify the input, then send it to a specialised path | Different request types need different handling |
| Parallelisation | Run independent calls at once, or vote across runs | Speed, or higher confidence on a decision |
| Orchestrator and workers | One model splits work and delegates to others | Tasks you cannot predict up front, like multi-file changes |
| Evaluator and optimiser | One call produces, another critiques, repeat | Clear quality criteria exist |
Combine these before reaching for open-ended autonomy.
Design tools like an API for a new teammate
A model reads your tool descriptions the way a new developer reads docs. Vague tools produce vague behaviour.
- Name tools by intent:
get_order_status, notquery. - Write the description for when to use it and when not to.
- Keep inputs small and typed. Reject invalid input with a clear error the model can recover from.
- Make tools idempotent where possible, so a retry does not charge a card twice.
- Return only what the model needs, not whole database rows.
- Separate read tools from write tools, and gate write tools.
A minimal agent loop in TypeScript
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const MAX_STEPS = 8;
const tools: Anthropic.Tool[] = [
{
name: "get_order_status",
description: "Look up the status of one order by id. Read-only.",
input_schema: {
type: "object",
properties: { orderId: { type: "string" } },
required: ["orderId"],
},
},
];
async function runTool(name: string, input: unknown): Promise<string> {
if (name === "get_order_status") {
const { orderId } = input as { orderId: string };
return JSON.stringify(await ordersApi.status(orderId)); // your real, permission-checked call
}
return "Unknown tool";
}
export async function runAgent(goal: string) {
const messages: Anthropic.MessageParam[] = [{ role: "user", content: goal }];
for (let step = 0; step < MAX_STEPS; step++) {
const response = await client.messages.create({
model: "claude-sonnet-5-5",
max_tokens: 1024,
system: "You help customers with orders. Never guess; use tools.",
tools,
messages,
});
messages.push({ role: "assistant", content: response.content });
if (response.stop_reason !== "tool_use") return response;
const results = [];
for (const block of response.content) {
if (block.type !== "tool_use") continue;
results.push({
type: "tool_result" as const,
tool_use_id: block.id,
content: await runTool(block.name, block.input),
});
}
messages.push({ role: "user", content: results });
}
throw new Error("Agent stopped: step limit reached");
}
The important parts are the step limit, the permission-checked tool implementation, and that every tool call flows through one place you can log and test.
Context engineering
What the model sees decides how well it behaves.
- Put stable rules in the system prompt: role, boundaries, tone, how to handle uncertainty ("say you do not know and offer to hand off").
- Retrieve, do not dump: fetch only the relevant documents (retrieval-augmented generation) instead of pasting everything.
- Trim history: summarise old turns and drop stale tool output.
- Be explicit about format: ask for structured output when code will consume it, and validate it.
- Keep instructions and data apart: mark untrusted content clearly as data.
Guardrails
- Least privilege. Give the agent only the tools it needs, scoped to the current user. A guest chatbot reads that guest's booking, never the database.
- Human approval for the irreversible. Refunds, deletions, deployments, emails to customers: propose, then wait for a person.
- Limits. Maximum steps, maximum spend, timeouts, rate limits.
- Validate outputs. Check tool arguments and model output against schemas before acting.
- Fail safe. When unsure, the agent hands off or asks, never improvises.
Security: prompt injection
Any text the agent reads can contain instructions: a web page, an email, a review, a support ticket. The model may follow them.
- Never rely on the model's good intentions to enforce permissions. Enforce them in the tool layer, where code checks who is calling and what they are allowed to do.
- Treat tool results and retrieved text as untrusted.
- Do not let one tool's output silently trigger a sensitive tool.
- Do not put secrets or card data in prompts.
- Log the actor as an AI agent so audits can tell human and automated actions apart.
Evaluate like software
You cannot improve what you cannot measure.
- Build a small set of realistic tasks with known good outcomes (a golden set), starting with your real failures.
- Check outcomes, not wording: did the right tool run with the right arguments, did the state change correctly?
- Use automated checks and tests wherever a correct answer exists. Use a model as a judge only for subjective qualities, and spot-check it with humans.
- Re-run the set whenever you change the prompt, tools or model.
Observability
Record every step: the prompt version, each tool call and result, token use, latency and the final outcome. A trace is how you debug "why did it do that" and how you spot cost spikes and loops.
Cost and speed
- Use the smallest model that passes your evals for each step.
- Cache stable prompt prefixes.
- Run independent steps in parallel.
- Cap steps and tokens.
- Stream partial results so users see progress.
Applying it to a portfolio assistant
Take a visitor-facing chat that answers questions about your work:
- Read-only tools only: projects, skills, experience, contact request.
- Retrieve from your own data, and refuse unrelated requests politely.
- Treat the visitor's message as untrusted, and never let it change tool permissions.
- Disclose clearly that it is an AI, label generated content, and offer a way to reach you directly.
- Log conversations without personal data, and review them to improve the prompt.
Common mistakes
- Starting with a fully autonomous agent when a workflow would do.
- Vague tool descriptions and tools that return huge payloads.
- No step or cost limit, so a loop burns money.
- Enforcing permissions in the prompt instead of in code.
- No evals, so every change is a guess.
Checklist
- Single call or workflow considered before an agent
- Every tool typed, described, scoped and idempotent where possible
- Step, time and spend limits in place
- Permissions enforced in code, not in the prompt
- Human approval for irreversible actions
- Golden-task evals run on every change
- Full traces recorded