Skip to content

Build1 publisher2 min readPublished

Microsoft's Agent Harness ends an autonomous run when the model types RESEARCH_COMPLETE

One extension method turns an IChatClient into a bounded agent loop with tool approval and history persistence already wired. The default exit condition is an exact marker string the model has to emit.

The Engineer · Build desk

Illustration accompanying Microsoft's Agent Harness ends an autonomous run when the model types RESEARCH_COMPLETE

What happened

  • Microsoft Agent Framework ships an Agent Harness, a runtime layer that turns an IChatClient into an agent able to run long jobs such as research, coding or data analysis using the framework's existing blocks.
  • Setup is one extension method, chatClient.AsHarnessAgent(), followed by an ordinary RunAsync call, which the post demonstrates with the prompt "Plan a weekend trip to Seattle."
  • Termination has to be configured: the sample sets LoopEvaluators to a CompletionMarkerLoopEvaluator looking for RESEARCH_COMPLETE and caps the run through LoopAgentOptions.MaxIterations.
  • Isaac Ojeda's dev.to walkthrough applies the Harness to TopicResearchAgent, a console app that breaks a topic into questions, searches Tavily from several angles, then compiles a technical report.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Teams maintaining a hand-written re-invocation loop now pick between keeping it and moving the stop condition into a framework options object that also brings tool approval and history persistence.
  • constraint A run's reliability rests on the model emitting one exact string, so the iteration cap becomes the working backstop and a missed match is billed as a full-budget run.
  • cost Budgeting is denominated in iterations, so anyone who prices agent runs in tokens or dollars has to add that accounting on top of MaxIterations.
  • capability An existing IChatClient can take on multi-step autonomous work without a second runtime to host. The adoption unit is a configuration change.

Call `AsHarnessAgent()` on an `IChatClient` and you get an `AIAgent` back [3]. It is the same type as the flat agent that takes one message, possibly calls tools, and answers [12]; the extra behaviour is switched on by configuration on that object [4]. According to Isaac Ojeda's dev.to walkthrough, the Harness composes pieces already in the framework instead of defining a parallel runtime [2][20].

The chat pipeline supplies function calling, message injection and history persistence [9]. Context providers carry instructions, memory, todos and operation modes [10]. Tool approval and observability are middlewares, and so is the bounded loop, which the post lists as optional [11].

The evaluator does the deciding. `CompletionMarkerLoopEvaluator` reads the model's last response and looks for the marker string exactly; while the marker is absent, the loop invokes the agent again [8].

A model that finishes its report without typing the marker keeps going to the cap, so the run costs the full `MaxIterations` in model invocations [18]. A model that puts the marker in its last response for any other reason stops there [21]. The only ceiling the post describes is that iteration count; there is no token or spend budget in the options it shows [19].

`HarnessInstructions` and `ChatOptions.Instructions` are separate fields, one for how to behave as an agent, including autonomy and tool discipline, the other for the domain role [5]. The harness block is always prepended to the domain block [6]. The ordering is fixed by the framework, so a domain instruction that contradicts the agent-behaviour text arrives second in the prompt.

Whether this absorbs the scaffolding in your codebase depends on the shape of your stop condition. If the agent can finish on a marker the model emits, or on a custom evaluator over the assistant's last message, the Harness loop replaces your `while` and your safety limit becomes an iteration count [13]. If your ceiling is denominated in tokens or dollars, or your completion test needs to read tool results, that code is still yours to maintain.

This is one practitioner walkthrough, not the framework documentation. It is honest about which decisions it hands over: Ojeda never tells the sample how many searches to run or in what order, and the agent decides iteration by iteration [16]. The full sample is published on GitHub as TopicResearchAgent [17].

What to watch

  • Whether Microsoft's documentation ships evaluators that read tool results rather than only the assistant's last message.
  • Whether a token or spend budget appears alongside MaxIterations in LoopAgentOptions.
  • Whether the bounded-loop middleware stays optional as the Harness moves through releases.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories