# What this sample demonstrates This sample demonstrates how to wrap a `HarnessAgent` with the **`LoopAgent`** decorator to re-invoke the agent until a configured **`LoopEvaluator`** decides to stop. A single decorator covers the common looping patterns — you just plug in a different evaluator (and optionally switch on fresh-context mode). The `HarnessAgent` pre-configures function invocation, per-service-call chat history persistence, and in-loop compaction, so each demo only supplies the chat client, token limits, and instructions, then wraps the result with a `LoopAgent`. ## Looping patterns showcased The program runs four demos sequentially, each driven by a different evaluator: | # | Pattern | Evaluator | Notes | | --- | --- | --- | --- | | 1 | Completion-marker ("Ralph"-style) loop | `CompletionMarkerLoopEvaluator` | Re-invokes until the agent emits `COMPLETE`. Uses `FreshContextPerIteration = true` to restart each pass from the original task plus the aggregated feedback log on a new session, and includes the `{last_response}` placeholder in the feedback template so the agent sees its previous suggestion even though each pass starts fresh. | | 2 | Delegate predicate (todos remaining) | `DelegateLoopEvaluator` | Loops while the built-in `TodoProvider` still has open items. The provider is fetched from the agent via `GetService()` and queried against the loop's current session. | | 3 | AI judge | `AIJudgeLoopEvaluator` | A second `IChatClient` judges whether the original request was fully answered and continues while the answer is "no", injecting its gap analysis as the next input. | | 4 | Approval heuristics + loop | `DelegateLoopEvaluator` + `ToolApprovalAgent` | Combines the `ToolApprovalAgent` auto-approval heuristics (`AutoApprovalRules`) with the loop, so a looped agent auto-approves tool calls instead of stalling on a pending approval. | `MaxIterations` caps every loop so it always terminates even if the evaluator never stops. ### Evaluator mapping (Python → .NET) The Python sample in [microsoft/agent-framework#6174](https://github.com/microsoft/agent-framework/pull/6174) exposes several distinct loop classes. In .NET these collapse into one `LoopAgent` that consumes evaluators: | Python | .NET | | --- | --- | | Ralph loop (completion marker) | `LoopAgent` + `CompletionMarkerLoopEvaluator` | | Ralph loop (fresh context each pass) | `LoopAgent` + `CompletionMarkerLoopEvaluator` + `FreshContextPerIteration = true` | | Callable / predicate loop | `LoopAgent` + `DelegateLoopEvaluator` | | AI judge loop | `LoopAgent` + `AIJudgeLoopEvaluator` | ## Prerequisites Before running this sample, ensure you have: 1. An Azure AI Foundry project with a deployed model (e.g., `gpt-5.4`) 2. Azure CLI installed and authenticated (`az login`) ## Environment Variables Set the following environment variables: ```bash # Required: Your Azure AI Foundry project endpoint export AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project" # Optional: Model deployment name (defaults to gpt-5.4) export AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4" ``` ## Running the Sample ```bash cd dotnet dotnet run --project samples/02-agents/Harness/Harness_Step05_Loop ``` ## What to Expect The program runs the four demos in order. Each loop is executed with `RunStreamingAsync`, so output is printed live and every re-invocation of the inner agent is marked with a `--- run N ---` header (detected via a change in the streamed `ResponseId`) — this lets you see exactly when the `LoopAgent` loops. Each streamed message is prefixed with `User:` or `Agent:` based on its role, so the loop's on-behalf-of feedback messages (surfaced as `User` turns) are visually distinct from the agent's responses (`Agent`). Each demo finishes by printing its aggregated final response. Demo 4 also prints an `Auto-approving: ...` line each time the `ToolApprovalAgent` heuristic approves the `DeployService` tool call, showing how approval-aware agents integrate with the loop. ## Security Considerations Demo 3 uses `AIJudgeLoopEvaluator`, which is an explicit opt-in to sending the original request and the agent's latest response to a second, external judge `IChatClient` on every iteration. A compromised or malicious judge endpoint could exfiltrate that data, or return a manipulated verdict/gap analysis that gets fed back into the loop as feedback — a form of indirect prompt injection. Only configure a judge client that points at a service you trust as much as the primary model.