I use a simple test before designing an agent: could ordinary code or a clear workflow solve this more reliably?
If the answer is yes, I use ordinary code. Agents are useful when the work is open-ended, the next step depends on what was found, and a model needs to choose between several tools. They are not a better name for every automation.
An agent receives a goal, chooses an action, observes the result, and decides what to do next.
flowchart LRG[Goal] --> P[Choose next step]P --> T[Call a tool]T --> C[Check the result]C -->|Continue| PC -->|Need approval| H[Human review]C -->|Complete| D[Return result]H --> P
The loop is easy to draw. The engineering work sits around it:
Without those controls, autonomy mostly means unpredictable failure.
I think about four levels.
Use a function when inputs, rules, and outputs are known. Calculations, validation, data transforms, and permission checks belong here. A model can help interpret an input, but it should not replace rules the business already understands.
Use a workflow when the steps are known but some steps need a model. For example: classify a document, extract fields, validate them, ask for approval, then update a system. The workflow owns the order and retries; the model handles bounded judgement inside a step.
This is my default for business automation because it is easier to test and operate than an open loop.
Use one agent when the goal is open-ended and the agent must choose tools or revise its approach. Research, support investigation, and coding assistance can fit this shape.
One capable agent with good tools is usually easier to understand than a team of agents. I add specialisation only when evaluation shows that one role is carrying too much context or making conflicting decisions.
Use multiple agents when separate roles need different instructions, tools, permissions, or review responsibilities. Splitting a task merely to make the diagram look sophisticated adds latency and more failure points.
| Pattern | Good fit | Main risk |
|---|---|---|
| Router and specialist | Requests fall into distinct domains | Wrong routing hides the right specialist |
| Sequential hand-off | Each stage produces a clear input for the next | Early errors flow through the chain |
| Parallel workers | Independent research or checks can run together | Duplicate cost and difficult merging |
| Worker and reviewer | Output has clear review criteria | Endless revision loops |
| Human approval | An action changes money, data, access, or commitments | Approval becomes a meaningless click |
A group chat is rarely my first choice. Free-form conversations between agents are difficult to test and can consume a great deal of context without improving the result. Explicit hand-offs and structured outputs are easier to reason about.
An agent cannot do more than its tools allow. I design tools as if they were public APIs:
For knowledge tools, I treat retrieval as a separate system. This guide to vector databases and hybrid search covers the design choices behind it.
I never rely on the model to enforce authorisation. The application checks the user’s identity and permissions for every tool call. Instructions such as “only access the current customer’s data” are useful context, not a security control.
These terms are often mixed together.
I persist workflow state outside the model so a failed process can resume. I keep context as small as the task allows. I only store memory with a clear purpose, retention rule, and deletion path. Saving every conversation forever is not a memory strategy.
Before release, I want answers to these questions:
I log model calls, tool inputs and outputs with sensitive fields removed, routing decisions, approvals, latency, and cost. A final answer alone is not enough to debug an agent.
In 2026, Microsoft Agent Framework provides code-first agents and graph-based workflows across .NET and Python, with integrations for Microsoft Foundry and other model providers. Foundry Agent Service adds managed hosting, identity, observability, and related platform services.
Those tools can reduce infrastructure work, but they do not decide the architecture. The same design rules apply if I use another framework or build the loop directly with a model SDK.
I keep domain tools and business rules in ordinary application code. Framework-specific orchestration stays at the edge. That makes it easier to test the important logic and change the agent runtime later.
I start with one narrow outcome, one agent or workflow, and a small set of read-only tools. I collect real examples, trace failures, and add a human approval step before any meaningful write.
Only after that works do I consider more tools, memory, or specialised agents. Complexity should answer a measured failure, not anticipate one.
Legal Stuff
