methodologyNov 10, 2026·7 min read

18 Months Building an Autonomous Agent: What Worked

By Jonathan Stocco, Founder

In early 2025, we started with a deceptively simple goal: build an AI system that could plan, execute, and self-correct without a human in the loop. By mid-2026, we had something that actually worked. What happened between those two points is the part nobody writes about honestly. This is that account.

The context matters. According to McKinsey's State of AI 2024 report, 72% of organizations now use AI in at least one business function, up from 50% in previous years. Most of that adoption is chatbots, classifiers, and retrieval systems. Genuinely autonomous agents, ones that decompose goals, manage their own tool calls, and recover from failure states, remain rare in production. We wanted to build one.

What We Set Out to Build

The original design was a general-purpose orchestration layer sitting on top of n8n. The idea: a reasoning model receives a high-level objective, breaks it into subtasks, routes each subtask to a specialized module, and assembles the results. Clean on a whiteboard. Painful in practice.

Our first working prototype used what we now call a flat architecture: three agents (research, scoring, writing) all reporting to a single orchestrator. I remember the first demo. Five inputs, clean outputs, everyone impressed. We shipped it internally and immediately started feeding it real volume.

At 50 concurrent inputs, the system stalled. The scoring module sat idle waiting on research outputs that had nothing to do with scoring. The orchestrator had no way to signal partial readiness. Everything queued behind everything else. We had built a synchronous waterfall and called it an agent.

That failure taught us the first real lesson: an orchestrator that holds all state is a single point of congestion, not a coordination layer.

What Went Wrong, and What We Changed

We rebuilt the architecture around discrete agents with explicit handoff contracts between them. Each module publishes a typed output schema. The next module in the chain only activates when its required inputs arrive, regardless of what else is happening upstream. This is what ForgeWorkflows now calls agentic logic: not a monolithic controller, but a graph of independent reasoning nodes that communicate through defined interfaces.

The change had two immediate effects. Processing throughput improved because modules ran in parallel wherever the dependency graph allowed. More importantly, each agent became independently testable. We could break the scoring module in isolation, fix it, and redeploy without touching research or writing. That kind of isolation is what makes a system maintainable past the prototype stage.

We also got the memory layer wrong twice. Our first attempt used a shared vector store that every agent could read and write. It seemed elegant. In practice, agents started retrieving each other's intermediate reasoning as if it were ground truth. The scoring module would pull a half-formed research note and treat it as a confirmed fact. We added write-access controls so each agent can only write to its own namespace and read from a curated shared context. Messy to implement, but it eliminated an entire class of hallucination we couldn't otherwise reproduce reliably.

The third failure was tool call management. Early on, we gave the reasoning model access to every available tool on every turn. The model would sometimes chain six tool calls to answer a question that needed one. We introduced a tool manifest per task type: each objective class gets a defined set of permitted tools, nothing more. This cut unnecessary API calls and made the system's behavior far more predictable during testing.

One honest limitation worth naming: this architecture works well when task types are known in advance and can be mapped to tool manifests. It breaks down when you need the system to handle genuinely novel objective classes it has never seen. For open-ended, unpredictable inputs, you need a different approach, likely a meta-agent that can construct its own tool manifests dynamically. We have not solved that problem cleanly. Anyone who tells you they have is probably overstating their results.

If you want to understand why generic orchestration layers tend to fail under real load, our post on why generic backends fail AI agents goes deeper on the infrastructure side of this problem.

Lessons That Held Up After 18 Months

Three things proved durable across every rebuild.

Explicit inter-agent schemas are non-negotiable. Implicit data passing, where one agent assumes it knows what the previous one produced, fails the moment you change either agent. Every handoff in our system is a typed contract. If the upstream output doesn't match the downstream input schema, the pipeline halts and logs the mismatch rather than passing garbage forward. This is tedious to set up. It saves enormous debugging time later.

Failure modes need to be first-class citizens in the design. We spent the first six months optimizing the happy path. The system looked great in demos and fell apart in production because we had no graceful degradation. Now every agent has a defined fallback: what it returns when its primary tool call fails, when the LLM returns a malformed response, when a dependency times out. The fallback outputs are often lower quality, but they are always valid. A system that fails gracefully is one you can actually operate.

Testability is an architectural property, not a testing strategy. You cannot bolt testability onto a system that wasn't designed for it. The modular architecture we arrived at, discrete agents with typed interfaces, made it possible to run each component against a fixed input set and assert on the output. We now test every agent module independently before any integration run. This is the same principle behind our Blueprint Quality Standard: a pipeline that cannot be tested in parts cannot be trusted as a whole.

The broader pattern across all three lessons is the same: the decisions that feel like overhead early (typed schemas, fallback states, isolated test harnesses) are the ones that determine whether you have a working system at month 18 or a prototype that only runs in controlled conditions.

Autonomous agents are not a solved problem as of mid-2026. The tooling around n8n and similar orchestration platforms has matured considerably, but the hard architectural questions, how agents share state, how they recover from failure, how you keep them from amplifying each other's errors, still require deliberate design choices. Theory gets you to the whiteboard. The 18 months gets you to production.

What We'd Do Differently

Start with the failure taxonomy before writing a single node. Before we built anything, we should have enumerated every way the system could fail and designed the response to each. Instead, we discovered failure modes reactively, which meant rebuilding parts of the architecture under pressure. A pre-mortem session at the design stage would have surfaced at least half of what we found the hard way.

Version the inter-agent schemas from day one. We treated our handoff contracts as internal implementation details early on, which meant schema changes broke dependent agents silently. Versioning them like public APIs, with a changelog and a deprecation window, would have saved weeks of debugging across multiple rebuilds.

Build the observability layer before the agents, not after. We added logging and tracing retroactively, which meant the first several months of production data were too sparse to diagnose real failures. If I were starting over, the first thing I would ship is a structured event log that captures every agent input, output, tool call, and fallback trigger. Everything else is easier to debug when you have that record.

Related Articles