Over the past 4 months of working with graphs, I’ve learned several major lessons about graph design the hard way. In this post, I want to share the main takeaways so you don’t repeat my mistakes.

First, my definition of graphs:

Agent graphs (a.k.a. workflows) are directed graphs that allow cycles and describe how work is passed between agents (nodes) operating in a loop through predefined transitions (edges). Graphs consist of branches, loops, scripts, and transitions (along with their prompts and parameters).

Parallelism is not the silver bullet

At first, I was very enthusiastic about parallel branches in graphs. But over time, I realized that parallelism can not only increase costs but also slow down task execution.

A standard parallel group of checks may include code review, QA, and scope review. The problem begins when these stages are inside a loop.

Let’s take a simple example. Suppose code review, QA, and architecture run in parallel, after which the task returns to implementation if necessary.

If the architecture review passes but the code review finds several minor issues, the task returns to the implementation agent. Once the fixes are made, it goes back for review - and the architecture reviewer has to examine the updated diff again, even though the previous version was completely acceptable.

In cyclic graphs, parallel checks often lead to duplicated work, cache invalidation, and unnecessary costs with no real benefit.

In theory, this problem can be solved with a smart router. Kent supports this through script nodes: the router can determine whether the agent completed the entire implementation or only addressed feedback from a specific reviewer (kent.sh is my free, open-source project for building agent graphs. I mention it because I use it myself and don’t know of any similar products. You can apply this advice to any comparable orchestrator).

However, this brings us back to the problem we were trying to avoid with agent graphs: the agent once again gets to decide which verification stages need to be run. This negates a significant portion of the graph’s value.

In practice, the solution is simpler: dependent checks should run sequentially. In my workflows, architecture review always comes before code review. The task moves on to code review only after the architecture has been approved.

That’s why I’ve removed many parallel stages and now save tokens by avoiding checks on results that would have been rejected at another stage anyway.

This approach works especially well with planning, code review, and QA. For example, code review should first filter out implementation issues, and only then should QA begin. Otherwise, both stages may independently find the same bug and produce duplicate feedback.

Agents must be able to challenge feedback

Initially, absolutism and dictatorship ruled my development agent graph: every reviewer comment had to be addressed, or the task could not proceed. But reviewers don’t always produce the right result either.

Now, every agent in my graphs can ask me a question and clarify what to do with conflicting feedback. For example, scope review may reject tests that code review had required just one step earlier because it considered task verification incomplete without them. At the same time, agents cannot be fully trusted to resolve such conflicts on their own. Even with new models like Sol, you can end up in an infinite loop of fixing made up or nitpick problems.

I solve this by delegating the final decision to myself (pure choice, I like to be involved). You can also hand it off to a PM agent or set up communication between multiple agents. For example in Kent agents can get others’ session IDs so they can discuss the situation and reach a compromise.

Anthropic in their recent paper argue that this is the model’s problem. I disagree - this is the harness’s problem, and my system above proves that.

A graph must have a mechanism for escalating conflicting or questionable feedback - otherwise, review turns into a dictatorship capable of trapping the entire workflow in a loop, or a war of stubborness.

Don’t forget static checks

Agent graphs sound exciting, and it’s easy to want to create dozens of agents and verification stages. This can indeed reduce the primary agent’s cognitive load and improve the quality of its work, but static checks should take priority.

Initially, my implementation agent ran the linter, architecture tests, and unit tests itself, opened the PR, and checked incoming comments. I realized at one point that that’s just cargo culting, then decided to move these actions into script nodes in the agent graph.

Now, a separate stage:

  • runs the required static checks and tests;
  • properly manages the machine’s shared resources;
  • filters the results;
  • returns only relevant information to the implementation agent;
  • invokes the agent again only when its involvement is actually required.

If the tests are green, the implementation agent never even learns about it: no new turn is started, which means the agent doesn’t spend a single token on running tests or reading their results.

Don’t assign an LLM work that a regular script can perform more reliably and cheaply. At workflow scale, this produces substantial savings.

Choose models appropriate for tasks

If you don’t optimize your graph for token usage and cost, you can significantly overspend simply because many tasks will be overkill under the updated workflow. In the past, we used one model for everything in harnesses because we had no alternative. You no longer need to do that, and properly allocating models and resources can save you a lot of money.

In standard harnesses, you can usually switch models, but doing so invalidates caches. On top of that, you either retain the cluttered context from the previous session or start a new one and steer/prompt it manually.

Kent solves these problems, so don’t be afraid to create different roles for agents. For example, manual QA can run on cheap models like DeepSeek or Luna, which cost almost nothing or barely affect your subscription quota. The smartest models can then be reserved for critical stages, such as planning.

It has long been known that if you have a good plan, you can assign implementation to a less capable model and get almost the same result. Moreover, additional verification stages reduce the minimum level of model intelligence required to implement a task even further.

Starting with version 2.6, Kent natively allows one agent to select the model, system prompt role, and reasoning level for the next agent after transitioning along a graph edge. This makes it possible to:

  • delegate simple tasks and bug fixes to models like Luna;
  • run QA on cheap models with high limits;
  • hand simple decisions off to local models;
  • reserve the strongest models for complex planning and critical checks.

Keep an eye on caches and time between turns

I measured the threshold beyond which the probability of continuing a session after a cache miss - and paying several times more - becomes high enough for preemptive compaction to be worthwhile.

Image speculative compaction (for regular sessions) becomes worthwhile at ~88% context usage according to this slop-chart. For workflows, my statistical threshold is around 71%

Imagine that the implementation agent spent 40 minutes addressing code review feedback. During that time, the reviewer agents’ caches may have been invalidated. When they review the work a second time, Kent will compact the session in advance so the review continues with fresh context and without unnecessary costs caused by a cache miss.

But this is only a heuristic. You should still consider how much time passes between consecutive calls to the same agent. If the workflow is long and a node waits a long time for the work to return, the likelihood of cache invalidation increases.

In this case, there are two main options:

  • use compact and continue mode in Kent - it is similar to speculative compact, but compaction is always performed;
  • create more granular checkpoints that return work to the agent more frequently and keep caches warm.

With the right setup, you can reduce costs so much that the average cost of completing a task is lower than working in a regular chat with the same Sol/Opus at standard reasoning.

If you ignore this, it’s easy to fall into the overkill trap and become disappointed with agentic graphs: “This is too expensive for me.” But in practice, well-designed agent graphs can be more efficient than standard sessions.

Make nodes idempotent

As my graph evolved, I added more and more ways to send a task backward. Different reviewers and stages gained the ability to return it to previous nodes. This gives agents the flexibility they need, for example, if the implementation agent receives a flawed plan, it should be able to return the task to the planning stage and explain exactly what needs to be fixed. As in regular software development, product issues and underspecified requirements are often discovered only during implementation.

That’s normal, but what’s not normal is a graph that gives the agent no way to handle such a situation. Every flawed line in a plan can potentially lead to thousands of lines of incorrect code.

But a non-obvious topological problem arises after the task returns to an earlier stage. Subsequent nodes may receive it with fresh context and a prompt implying that the work should start from scratch. For example, the implementation agent returns an unfinished task for replanning, then receives an instruction to implement the updated plan as though no previous work existed.

This can cause duplication, conflicting implementations in the same codebase, and wasted money - and not in the form of an obvious workflow failure, but through subtle issues like “weirdly many git commits on the PR”. It’s also a common mistake made by agents themselves when they build workflows for you, including Kent. Agents struggle to analyze topology in the context of prompting - to put themselves in the shoes of the agent doing the actual work.

Re-entering a node should not automatically mean repeating all the work from scratch. The agent must account for the existing result and continue from the current state.

Kent supports this natively: for implementation-related nodes, you can enable the continue or new continuation mode.

Prompts should also be adapted: explicitly state that receiving a task again does not mean the agent needs to start over. Kent already adds the relevant instructions to agent prompts during a workflow, but custom prompts may still implicitly assume that the work begins from scratch, and that can cause the model to freak out REALLY hard.

Idempotent nodes, controlled returns, and proper context reuse make an agent graph resilient not only to model errors but also to the real-world nonlinearity of development.