Introduction
Part II gave our support agent tools, MCP, conversation sessions, and retrieval. Those capabilities answer questions, but a production agent also needs to manage work that takes several steps and may change business state.
This article adds an agent harness around the same AIAgent foundation. The harness owns a plan, records progress, pauses before a sensitive tool call, and resumes the same session after a human approves or rejects the action. Planning is explicit application state; the model may propose steps, but it does not get authority to execute them.
The sample handles a duplicate-payment request:
|
|
The agent can inspect the order immediately. A refund changes financial state, so the harness creates an approval request instead of calling the refund operation.
Real-World Scenario: A Duplicate Charge
The support team receives this request:
|
|
The agent must not jump directly from text to a refund. It needs to:
- Identify and validate the order.
- Check the order and payment facts.
- Explain why a refund is being considered.
- Ask an authorized employee to approve the financial action.
- Execute the refund once, then report the result.
The customer request and the approval decision may happen in different HTTP requests, or even on different machines. That is why this is a harness problem rather than only a prompt-writing problem.
1. Agent Harness: Runtime Control Boundary
An agent harness is the runtime boundary around an agent run. It coordinates the parts that are easy to lose when an agent grows beyond one prompt:
- Session and plan state.
- Tool selection and execution policy.
- Progress, failures, and resumability.
- Context limits and persistence.
- Approval requests for sensitive operations.
- Logging, tracing, and audit records.
The harness does not make the model reliable by itself. It makes important decisions visible and enforceable in application code.
Microsoft’s current Agent Harness guidance describes a fuller runtime with planning/execution modes, todo tracking, context handling, approvals, persistence, and telemetry. This sample keeps the boundary small so every state transition is visible. It is an application-level harness, not a replacement for the framework’s official harness package.
2. Planning: Controlled Decomposition
Why planning matters
A single model response hides whether the agent inspected the right data, skipped a required check, or attempted an unsafe action. A plan turns a vague goal into observable work:
|
|
The plan is not a promise that every step will succeed. It is a checkpointed state machine. Each step should have a clear owner, bounded inputs, and a durable status.
Planner versus executor
Keep two responsibilities separate:
| Responsibility | Owner |
|---|---|
| Interpret customer goal | Model and agent |
| Propose useful steps | Planner |
| Validate allowed steps | Harness policy |
| Execute business operation | Application tool |
| Decide sensitive approval | Human or policy service |
| Persist progress | Harness store |
Do not let free-form model text become executable instructions. In the sample, the planner creates a fixed plan for the refund scenario. A model can later propose a plan, but the harness must validate its step types against an allow-list before persistence.
3. Human Approval: Gate Sensitive Tools
Approval is a pause, not a prompt instruction
Adding “ask for approval” to system instructions is not an authorization boundary. The application must prevent the sensitive function from running until an approval decision exists.
Approval records should include the request ID, session ID, tool name, validated arguments, requester, approver, decision, timestamp, and reason. Never accept the tool arguments again from an untrusted approval callback without comparing them with the persisted request.
Which tools require approval?
Usually require approval for:
- Refunds, payments, transfers, and cancellations.
- Sending external messages or publishing content.
- Account, permission, or identity changes.
- Deleting or exporting customer data.
- Any operation whose impact is difficult to reverse.
Read-only order lookup and policy retrieval can run automatically when authentication, authorization, input validation, and rate limits are still enforced.
Build the Approval Flow
The sample uses a small set of records and an in-memory store:
|
|
The harness controls transitions and calls the side-effecting operation only after it has checked the persisted decision:
|
|
The important detail is ownership. The endpoint does not call RefundOrder directly. It asks the harness to run or resume a plan, and the harness is the only path that can invoke the side-effecting tool.
4. Context and Resumability
An AgentSession carries conversation context between agent runs. A harness adds operational state around that conversation: plan steps, approval requests, tool results, and failure checkpoints.
Persist both when a workflow can outlive an HTTP request. The sample uses an in-memory store for clarity, so restarting the process loses pending approvals. A production implementation should use a durable store, tenant and user authorization, optimistic concurrency, and an expiration policy for stale approvals.
Long conversations also need compaction. Keep system instructions and recent decisions, summarize old turns, and retain references to source records rather than copying unlimited tool output into every prompt. Context compaction reduces tokens; it does not replace audit storage.
5. Middleware and Failure Boundaries
Use middleware or an equivalent pipeline for cross-cutting checks:
- Validate session ownership and request size.
- Add correlation and trace information.
- Enforce tool allow-lists and timeouts.
- Convert transient failures into a resumable failed step.
- Record approval and tool events.
Do not assume every failed model or tool call should be retried. Read-only lookups may use bounded exponential backoff. Refunds need idempotency keys and reconciliation, not blind retries.
The Sample Application
The Part III sample is a small ASP.NET Core API built on the same Microsoft.Agents.AI package used in Parts I and II:
SupportAgentwrapsAIAgentfor optional explanations.AgentHarnessowns plan, approval, and idempotent refund state.AgentHarness.Apiexposes run, approval, and plan endpoints.
Run the Aspire host to start the API and the Redis redis:7.4 approval-store container:
|
|
Start a plan:
|
|
The response is 202 Accepted with an approval request. Approve it using the returned request ID:
|
|
Rejecting the request records a terminal rejection and never invokes the refund tool:
|
|
The sample includes unit and API integration layers matching Parts I and II:
|
|
Production Checklist
- Persist sessions, plans, and approval records durably.
- Authorize every read and write by tenant, user, and resource.
- Validate model-produced arguments with typed schemas.
- Use idempotency keys for side effects and reconcile uncertain results.
- Give approval requests an expiry and handle duplicate callbacks.
- Trace model calls, tool calls, plan transitions, and approval decisions.
- Redact payment and personal data from logs.
- Bound plan length, tool calls, tokens, latency, and cost.
- Test rejected, expired, duplicated, timed-out, and partially completed runs.
Conclusion
An agent harness gives multi-step work a durable control boundary. Planning makes work observable, sessions and checkpoints make it resumable, and approval gates keep sensitive tools behind an explicit human decision.
The model remains useful for interpretation and proposing the next step. The harness owns policy, state transitions, and authority. That division lets the support agent become more capable without turning a generated response into an uncontrolled business operation.
Run the Complete Example
The complete sample is available here: