AIARTICLE

4 engineering patterns that separate a real AI agent from a prompt with a fancy name

Google analyzed thousands of submissions to the AI Agents Challenge and distilled four architecture decisions that showed up in the winners. None of them depend on a bigger model.

4 engineering patterns that separate a real AI agent from a prompt with a fancy name
Image: Alan Andrade

Google wrapped up another round of the Google for Startups AI Agents Challenge, and according to the post published on the Google Developers Blog by Sergio Villani, the most frequent label in the submissions was "multi-agent system." The problem: a lot of what got tagged multi-agent was, in practice, "a single model passing through a chain of prompts with agent names hung on them." The ones that made it to the top of each track repeated the same handful of engineering decisions, and that's what matters for builders: these are patterns that don't require a bigger team or a newer model.

Below are the four, with what each one solves, what it replaces, and where it doesn't pay off.

Pattern 1: MCP in Both Directions

Most teams used the Model Context Protocol (MCP) in only one direction: the agent calls a tool server to fetch data. One team did both sides. The agent consumed a telemetry database through its own MCP tool layer and then exposed that same reasoning as an MCP server that other agents could call directly, with no human-facing chat UI in between.

The inbound half is already worth it on its own. The naive version of this agent would run a SELECT against the telemetry database and dump every row into the model's context, which is exactly how a single request blows the token budget on a production database. Going through an MCP tool layer gives the agent a way to inspect and filter programmatically, pulling a specific job's execution plan or a specific stack trace instead of the whole table. The context stays small enough for the model to actually reason.

That mediation is also what makes the outbound half safe. A tool that only returns a limited, purpose-built response can be handed to a caller you don't control; a raw SQL connection, never. Once the agent's reasoning already lives behind a tool interface, exposing it externally just means standing up an MCP server in front of the same tools. In the case described, a code agent running in the terminal or IDE could ask the performance agent directly about a specific job, the same way it would call any other tool. Nobody had to open a dashboard, describe the problem in a chat, and copy the answer back.

A chat interface is a destination; an MCP server can be infrastructure that other agents build on, without anyone having to write a second integration for them.

The detail that's easy to skip: the moment you serve a caller you don't control, that server needs real access control. Whoever reaches the server is now calling your reasoning layer directly. A tool surface that only your own agent uses doesn't need to think about this; one exposed to the world does.

Pattern 2: Event-Driven Concurrency

One team's first version was a linear pipeline: the sensor agent called the compliance agent, which called the resident-messaging agent, which called the dispatch agent. It worked as a demo and fell apart on the real use case, which was detecting fall risk from a change in gait, cross-referencing it against a live drug-interaction database, and alerting the right person before the action window closed.

The fix was an asynchronous event bus built on top of four separate asyncio.Queue instances, one per agent, each with its own worker coroutine. Instead of Agent A calling Agent B and waiting for a return, agents publish typed events to named topics and subscribe to the ones they care about. A drop of 15% or more in gait speed publishes a CLINICAL.ANOMALY_DETECTED event. The compliance agent is already parked on that topic, picks up the event the instant it fires, cross-references the interactions database, and publishes its own CLINICAL.COMPLIANCE_REPORT_READY as soon as it's done, with no polling and no waiting on an explicit handoff from anyone upstream.

The concrete difference between a call chain and an event bus:

| | Call chain | Topic-based event bus | |---|---|---| | Total latency | Additive (sum of all agents) | Independent agents run in parallel | | Blocking | Each one holds the stack waiting for the next | No one blocks waiting on another's return | | Bottleneck | The slowest agent holds up the fastest | Each agent runs on its own time |

This format pays off where agents genuinely run on different timescales: one polling every few seconds, another making a half-second network call, another that fires just once at the end. Chain everything into a single stack and the fastest one gets stuck behind the slowest. As the post puts it, "a single-threaded system wearing a multi-agent label" is exactly what shows up when one agent has to wait on another to react to the same signal.

Pattern 3: Fallback That Still Has to Clear Your Bar

One team's clinical reasoning agent ran on Gemini 3.1 Pro. Under real load, Pro started returning 503s. The common response would be to bolt a retry loop onto the same model. Instead, the team set up a fallback to Gemini 3.6 Flash with backoff and ran the response from either model through the same validation function before accepting it: a citation check confirming that the response named an actual clinical guideline, not just plausible-sounding medical language.

The point isn't that the fallback exists, it's where the validation lives. It isn't duplicated, one copy for the primary path and another for the fallback path, where it's easy to update one and forget the other. There's a single validate_clinical_response() that both the Pro path and the Flash path are required to call before any result leaves the agent. Once the response hits that function, it doesn't matter which model produced it: neither gets a shortcut, and neither ships a response that fails the check just because it happened to be the model available at the time.

That's what keeps a fallback from lowering the bar without anyone noticing: it isn't about remembering to apply the same pattern twice, it's making it structurally impossible to apply it only once. If the code that runs after the fallback fires skips a validation step that the primary path has, you're shipping two products and testing one.

Pattern 4: Layered Routing Before the Expensive Call

Inference cost is probably the most discussed constraint in AI right now: everyone wants frontier-model reasoning without frontier-model pricing on every request. One team measured what was actually eating the budget and found it wasn't the hard questions, it was the easy ones ("where's my order", "cancel my appointment") going through the same full model call as genuinely ambiguous requests.

The solution was a three-layer classifier in front of the agent:

  1. Local regex captures navigational intent at zero tokens.
  2. Ambiguous cases go to a cheap Gemini call at 10 tokens and temperature 0.1, just to classify intent.
  3. Only what survives both steps reaches the full reasoning model.

By the team's own measurement, the first pass alone resolved more than 40% of messages before any call to a real model. Another submission applied the same idea with a fast, cheap model triaging the case and escalating to the slow, expensive model only what needs deep reasoning. The lesson is simple: don't spend your most expensive model on a decision a cheaper one already solves. Before assuming you need a bigger model, look at the distribution of your traffic.

What This Means for Those Building in Brazil

None of the four patterns depend on a large team or a new model, and they compose well with each other. The post highlights one team that combined patterns 1 and 3 in the same build: a root agent fanning out to specialists in parallel and then exposing that entire reasoning layer as an MCP server callable by other agents.

It's worth flagging the source's bias: Google notes that the patterns showed up more often in submissions built on the Agent Development Kit (ADK) and run through the Agents CLI, "because the framework doesn't fight you on concurrency, fallback, or handing a tool to another agent." Translating for the skeptical dev: the patterns themselves are framework-agnostic (an event bus with asyncio, a shared validation function, a regex classifier plus a cheap call all run on any Python stack), but the post comes from the maker of ADK, so treat the tool recommendation for what it is.

The core takeaway survives the marketing: the difference between a real agent and a pipeline of prompts wearing a badge lies in mundane software-engineering architecture decisions, access control, concurrency, single-point validation, and cost-based routing, not in which model you called.

Translated from the Brazilian Portuguese original · Read the original

View profile →