Белешка
Приступ овој страници захтева ауторизацију. Можете покушати да се пријавите или промените директоријуме.
Приступ овој страници захтева ауторизацију. Можете покушати да промените директоријуме.
Agent looping re-invokes an agent until a completion condition is satisfied. Use it for iterative refinement, todo completion, waiting for background tasks, or evaluating whether an answer meets explicit criteria.
Always bound autonomous loops. A completion condition can fail, a model can stall, and an evaluator can be probabilistic.
Important
Agent looping is experimental.
Set up looping manually
Use the direct composition API when you want looping without the other Harness Agent defaults.
Import the loop types and wrap any AIAgent with LoopAgent. Its default maximum is 10 agent invocations:
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
AIAgent baseAgent = chatClient.AsAIAgent();
AIAgent agent = new LoopAgent(
baseAgent,
new CompletionMarkerLoopEvaluator("DONE"),
new LoopAgentOptions { MaxIterations = 5 });
Import AgentLoopMiddleware and add it to a regular Agent. The default maximum is 10 agent runs:
from agent_framework import Agent, AgentLoopMiddleware
def needs_more_work(*, last_result, **kwargs):
return "DONE" not in last_result.text
agent = Agent(
client=client,
middleware=[
AgentLoopMiddleware(
needs_more_work,
max_iterations=5,
)
],
)
The predicate can be synchronous or asynchronous. Return True to continue, False to stop, or (continue, feedback) to pass feedback to the next iteration.
Go provides bounded reinvocation through agent/harness/loop. Register loop.New(loop.Config{...}) in agent.Config.Middlewares. The middleware runs the agent, evaluates its response, and optionally sends feedback into another iteration.
The following Foundry sample uses a completion marker. It requires FOUNDRY_PROJECT_ENDPOINT, FOUNDRY_MODEL, and an Azure credential:
// Copyright (c) Microsoft. All rights reserved.
package main
import (
"context"
"github.com/microsoft/agent-framework-go/agent"
"github.com/microsoft/agent-framework-go/agent/harness/loop"
"github.com/microsoft/agent-framework-go/examples/internal/demo"
"github.com/microsoft/agent-framework-go/provider/foundryprovider"
)
var logger = demo.NewLogger(
"Loop Harness",
"This sample shows how the loop harness keeps re-invoking an agent until it "+
"signals completion with a marker, so the agent can work across several turns "+
"on its own before returning.",
"Model", demo.FoundryModel,
)
// completionMarker is the token the agent is asked to emit once the task is
// fully done; the loop stops as soon as it appears in a response.
const completionMarker = "TASK COMPLETE"
func main() {
token := demo.FoundryTokenCredential()
// Wrap the agent with the loop harness: after each iteration the
// completion-marker evaluator decides whether to run the agent again.
a := foundryprovider.NewAgent(
demo.FoundryProjectEndpoint,
token,
foundryprovider.ModelDeployment(demo.FoundryModel),
foundryprovider.AgentConfig{
Instructions: "You are a diligent assistant. Work on the request step by step across " +
"multiple turns. End your final message with the marker '" + completionMarker +
"' once, and only once, the request is fully complete.",
Config: agent.Config{
Name: "Worker",
Middlewares: []agent.Middleware{
// The loop re-invokes the agent (and everything below it,
// including the logger) until an evaluator stops the loop.
loop.New(loop.Config{
Evaluators: []loop.Evaluator{
loop.NewCompletionMarkerEvaluator(loop.CompletionMarkerConfig{Marker: completionMarker}),
},
}),
logger, // for logging each agent iteration
},
},
},
)
ctx := context.Background()
resp, err := a.RunText(ctx, "Draft a short checklist for planning a small birthday party, "+
"then review it once for anything missing, and finish.").Collect()
demo.Response(resp, err)
}
MaxIterations is an optional *int and defaults to 10. Set an explicit bound for your task. With multiple evaluators, the first evaluator requesting another iteration supplies its feedback; the loop stops only when all evaluators stop or the iteration limit is reached.
For a credential-free example, see the scripted loop sample. It demonstrates marker completion, a custom evaluator, and the iteration safety cap.
Choose a completion condition
LoopAgent accepts one evaluator or an ordered collection:
| Evaluator | Continues while |
|---|---|
CompletionMarkerLoopEvaluator |
The latest response doesn't contain the configured marker. |
TodoCompletionLoopEvaluator |
A resolved TodoProvider still has incomplete items, optionally in selected agent modes. |
BackgroundTaskCompletionLoopEvaluator |
A resolved BackgroundAgentsProvider still has running tasks. |
AIJudgeLoopEvaluator |
A separate judge client says the original request isn't fully answered. |
DelegateLoopEvaluator |
Your callback returns LoopEvaluation.Continue(...). |
When multiple evaluators are configured, they run in order. The first evaluator that requests another iteration supplies its feedback; the loop stops only when all evaluators decline to continue.
Use an AI judge
The judge receives the original request and latest agent response. If it finds a gap, its analysis becomes feedback for the next iteration:
var evaluator = new AIJudgeLoopEvaluator(
judgeClient,
new AIJudgeLoopEvaluatorOptions
{
Criteria =
[
"Answer every part of the request.",
"Support conclusions with evidence.",
],
});
AIAgent loopAgent = new LoopAgent(
agent,
evaluator,
new LoopAgentOptions { MaxIterations = 4 });
Only use a judge endpoint you trust with the original request and generated response.
Control context and output
By default, LoopAgent reuses one session and sends the winning evaluator's latest feedback as the next input. FreshContextPerIteration = true instead rebuilds each pass from the original request plus an aggregated feedback log and resets or restores the session.
Non-streaming runs return an aggregated transcript by default. Set NonStreamingReturnsLastResponseOnly = true to return only the final response. Streaming always emits every iteration and any visible on-behalf-of feedback messages.
The predicate receives keyword arguments including iteration, last_result, messages, original_messages, session, agent, progress, and feedback. The helpers todos_remaining() and background_tasks_running() provide built-in todo and background-task conditions. Pair them with todos_remaining_message or background_tasks_running_message to generate a targeted next input.
Use an AI judge
AgentLoopMiddleware.with_judge builds a judge-driven loop. Judge loops default to five iterations:
from agent_framework import Agent, AgentLoopMiddleware
loop = AgentLoopMiddleware.with_judge(
judge_client,
criteria=[
"Answer every part of the request.",
"Support conclusions with evidence.",
],
max_iterations=4,
)
agent = Agent(
client=client,
middleware=[loop],
)
The judge's reasoning is fed back to the agent when more work is required. Only use a judge endpoint you trust with the original request and generated response.
Control context, progress, and output
For advanced loops, construct AgentLoopMiddleware directly:
record_feedbackcreates a concise progress entry after each work iteration.progressexposes accumulated entries to callbacks.inject_progress=Trueadds progress to the next iteration's input.fresh_context=Truerestarts from the original task and progress log and restores an attached session to its pre-loop snapshot.return_final_only=Truereturns only the last response for non-streaming runs.
Pass max_iterations=None only when the completion predicate is guaranteed to terminate.
Use loop.NewCompletionMarkerEvaluator to stop when the latest response contains a configured marker. For application-specific completion, implement loop.Evaluator or use loop.EvaluatorFunc, returning loop.Stop(), loop.Continue(feedback), or loop.ContinueWithMessages(messages).
FreshContextPerIteration restarts from the original input plus accumulated feedback. NonStreamingReturnsLastResponseOnly limits a non-streaming result to the final iteration.
Go doesn't include the built-in AI-judge evaluator shown in the C# examples. Supply your own evaluator if completion requires another model or an external check.
Use looping with Harness Agent
Use the Harness Agent setup when you also want its preconfigured history, planning, memory, approval, and observability pipeline.
Set HarnessAgentOptions.LoopEvaluators. The harness applies LoopAgent as its outermost agent decorator:
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
var options = new HarnessAgentOptions
{
LoopEvaluators =
[
new CompletionMarkerLoopEvaluator("DONE"),
],
LoopAgentOptions = new LoopAgentOptions
{
MaxIterations = 5,
},
};
HarnessAgent agent = chatClient.AsHarnessAgent(options);
// Equivalent construction: new HarnessAgent(chatClient, options)
AgentSession session = await agent.CreateSessionAsync();
An empty or null LoopEvaluators collection leaves the harness single-shot.
Approval and session behavior
LoopAgent stops before evaluating its completion condition when an iteration returns a pending tool-approval request. It returns the request to the caller instead of hiding it behind another autonomous iteration. After the caller supplies the approval response through the normal tool approval flow, the agent can continue.
LoopAgent doesn't add approval handling itself. The Harness Agent applies the loop outside ToolApprovalAgent, allowing pending approval requests to escape the loop.
Reuse the same AgentSession across calls to continue the conversation. Loop iterations share that session by default. With FreshContextPerIteration = true, LoopAgent resets or restores caller-supplied session state where supported. Service-owned conversation storage can retain history when the serialized session contains only a remote conversation identifier.
Supply loop_should_continue to create_harness_agent; loop_max_iterations defaults to 10:
from agent_framework import create_harness_agent
def needs_more_work(*, last_result, **kwargs):
return "DONE" not in last_result.text
agent = create_harness_agent(
client=client,
loop_should_continue=needs_more_work,
loop_max_iterations=5,
)
session = agent.create_session()
loop_next_message customizes the next input. With no loop_should_continue, the factory doesn't add a loop and ignores the other loop arguments.
Approval and session behavior
AgentLoopMiddleware stops before evaluating its continuation predicate when an iteration returns a pending tool-approval request. It returns the request to the caller instead of hiding it behind another autonomous iteration. After the caller supplies the approval response through the normal tool approval flow, the agent can continue.
AgentLoopMiddleware doesn't add ToolApprovalMiddleware itself. The Harness Agent places the loop outside its approval middleware, allowing pending approval requests to escape the loop. Create and pass an AgentSession on every Harness Agent run while tool auto-approval is enabled.
Reuse the same AgentSession across calls to continue the conversation. Loop iterations share that session by default. With fresh_context=True, the middleware restores the attached session to its pre-loop snapshot between iterations. Service-owned conversation storage can retain history when the serialized session contains only a remote conversation identifier.
The Go harness.AgentConfig helper doesn't add a loop by default. Include loop.New(...) in the caller's agent.Config.Middlewares to wrap the configured agent with bounded reinvocation.
The loop returns when an iteration produces pending tool-approval requests. It doesn't approve tools automatically. Handle the requests in the host and return the matching approval responses through the same session before continuing.