Building an Agent Harness in .NET: Orchestrator and Sub-Agents with Dedicated Context

Learn how to build a production agent harness in .NET, with an orchestrator, specialized sub-agents, dedicated context windows, and durable state.

By Ajith Joseph · Sun Aug 02 2026 · Updated Sun Aug 02 2026 · 5 min read · intermediate

#dotnet #llm #architecture #ai-agents

Everyone talks about multi-agent systems. Almost nobody explains the plumbing underneath them. That plumbing has a name now: the agent harness. It is the software that coordinates tool execution, manages memory, and persists state across sessions.

Most harness writing assumes Python. If your stack is .NET, you are on your own. This post fixes that.

What a Harness Actually Owns

Before writing code, get clear on responsibilities. The harness is not the agent. The harness is what the agent runs inside.

A production harness owns four things:

  • Tool execution, including timeouts, retries, and result serialization
  • Context assembly, deciding what enters each agent's window on each turn
  • State persistence, so a task survives a process restart
  • Budget enforcement, capping turns, tokens, and wall-clock time

If any of those live inside your prompt instead of your code, you do not have a harness. You have a chatbot with ambitions.

The Orchestrator and Sub-Agent Split

The core architecture is straightforward. An orchestrator agent coordinates specialized sub-agents, each with its own dedicated context, working in parallel where possible.

Why split at all? Because complex tasks exceed what a single context window handles well. Cramming a planning prompt, six tool schemas, retrieved documents, and a growing conversation into one window degrades everything.

A reasonable split for a document-processing pipeline:

  • Planner: decomposes the request, holds no tool schemas, small context
  • Retriever: owns vector search tools, sees the query and nothing else
  • Executor: owns write operations, sees the plan and retrieved context
  • Evaluator: reviews the executor output against the original request

Each sub-agent sees only what it needs. The orchestrator holds the thread.

Modeling It in C#

Start with a contract that makes context ownership explicit.

The Agent Abstraction

Define an agent as something that takes a scoped context and returns a result plus any state deltas. Keep the interface narrow. Resist the urge to give agents access to a shared mutable bag of everything.

Key design points:

  • Pass context in, never let agents reach into ambient state
  • Return results as typed records, not loose strings
  • Make tool sets an explicit constructor dependency per agent
  • Give every agent a hard turn budget it cannot exceed

Tool Registration

Use the built-in dependency injection container. Register each tool as a named service and compose per-agent tool sets at startup. This gives you a compile-time view of which agent can touch which system, which matters enormously for review and audit.

Wrap every tool invocation in a policy that handles timeout, retry with backoff, and structured error return. Tools should never throw into the agent loop. They should return a result the model can reason about.

Context Assembly

This is where most implementations go wrong. Do not append everything to a growing list. Build the context fresh on each turn from named sources:

  • System instructions, static per agent
  • Task specification, static per run
  • Retrieved knowledge, recomputed per turn
  • Recent turn history, windowed and summarized past a threshold

Making assembly a pure function of named inputs means you can unit test it. That alone catches a whole class of production bugs.

Durable State Is Not Optional

Long-horizon tasks run for minutes or hours. Processes restart. Deployments happen mid-run.

Persist after every turn, not at the end. A simple schema works:

  • Run record with status, budgets consumed, and current phase
  • Turn records with agent name, inputs hash, output, and tool calls
  • Artifact records for anything the run produced

With that in place you get resumability, full replay for debugging, and an audit trail for compliance. Entity Framework Core handles this fine. You do not need anything exotic.

Human Approval Gates

Some actions should never happen without a person in the loop. Human approval matters most for high-impact, irreversible actions, because capable agents can still act on incomplete context.

Implement gates as a first-class harness concept, not a prompt instruction:

  • Mark specific tools as requiring approval at registration time
  • When the agent requests one, persist a pending action and suspend the run
  • Resume only when an approval record appears

Because the gate lives in the harness, no prompt injection can talk its way past it. This is the single highest-value control you can add.

Observability From Day One

You cannot debug what you cannot see. Emit a structured trace per turn containing agent name, token counts, tool calls with durations, and the assembled context hash.

Watch three signals in particular:

  • Turns per completed task, trending up means degradation
  • Tool error rate by tool, spikes usually mean an upstream change
  • Budget exhaustion rate, high values mean your decomposition is wrong

Conclusion

An agent harness in .NET is ordinary software engineering applied to an unusual runtime. The principles that matter:

  • Give every sub-agent a dedicated, explicitly assembled context
  • Keep tool execution, budgets, and approval gates in code, never in prompts
  • Persist state every turn so runs survive restarts
  • Instrument turns, tools, and budgets before you need them

Build the harness first and the agents get simpler. Build the agents first and you will rewrite everything twice.

Get started by mapping your current agent's responsibilities against the four harness duties above. Whatever is currently living in your system prompt is your first refactor.

  1. AJ's Tech Notes
  2. Building an Agent Harness in .NET: Orchestrator and Sub-Agents with Dedicated Context