Human-in-the-Loop with kagent#
AI agents that can take action are powerful — but you don't always want them acting without your say-so. This tutorial walks you through building a Kubernetes-native AI agent that pauses and asks for your approval before doing anything destructive.
What You'll Build#
By the end of this tutorial, you'll have an agent running on a local Kind cluster that:
- Reads cluster resources freely (no approval needed)
- Pauses for your approval before creating, modifying, or deleting resources
- Asks you questions when it needs more information
Prerequisites#
Make sure you have these installed before starting:
| Tool | Install Link |
|---|---|
| Docker | get-docker |
| Kind | quick-start |
| kubectl | install-tools |
| Helm | install |
You'll also need an OpenAI API key.
Step 1 — Create a Kind Cluster#
Spin up a local Kubernetes cluster:
kind create cluster --name kagent-hitl
Verify it's running:
kubectl cluster-info --context kind-kagent-hitl
You should see the control plane address printed. If so, you're good to go.
Step 2 — Install kagent#
There are two Helm charts to install: the CRDs first, then kagent itself.
Install the CRDs:
helm install kagent-crds oci://ghcr.io/kagent-dev/kagent/helm/kagent-crds \--namespace kagent \--create-namespace
Set your API key:
export OPENAI_API_KEY="your-api-key-here"
Install kagent:
helm install kagent oci://ghcr.io/kagent-dev/kagent/helm/kagent \--namespace kagent \--set providers.default=openAI \--set providers.openAI.apiKey=$OPENAI_API_KEY
Wait for everything to come up:
kubectl wait --for=condition=ready pod --all -n kagent --timeout=120s
Once all pods report condition met, move on.
Step 3 — Deploy the Agent#
HITL is just additional configuration on the agent's tool references. You can enable HITL on a new agent, or add the requireApproval list to an existing agent's spec.declarative.tools entries to gate specific tools without changing anything else about the agent.
This step is the core of the tutorial. You create an agent that uses kagent's built-in Kubernetes tools (served via the kagent tool server), with approval gates on the destructive ones.
Save this as hitl-agent.yaml:
apiVersion: kagent.dev/v1alpha2kind: Agentmetadata:name: hitl-agentnamespace: kagentspec:description: A Kubernetes agent with human-in-the-loop approval for destructive operations.type: Declarativedeclarative:modelConfig: default-model-configsystemMessage: |You are a Kubernetes management agent. You help users inspect and manageresources in the cluster. Before making any changes, explain what youplan to do. If the user's request is ambiguous, use the ask_user toolto clarify before proceeding.tools:- type: McpServermcpServer:name: kagent-tool-serverkind: RemoteMCPServerapiGroup: kagent.devtoolNames:- k8s_get_resources- k8s_describe_resource- k8s_get_pod_logs- k8s_get_events- k8s_get_resource_yaml- k8s_apply_manifest- k8s_delete_resource- k8s_patch_resourcerequireApproval:- k8s_apply_manifest- k8s_delete_resource- k8s_patch_resource
Create the agent:
kubectl apply -f hitl-agent.yaml
Here's what each tool does:
| Tool | What Happens |
|---|---|
k8s_get_resources | Runs immediately — lists pods, services, deployments, etc. |
k8s_describe_resource | Runs immediately — shows resource details |
k8s_get_pod_logs | Runs immediately — reads pod logs |
k8s_get_events | Runs immediately — shows cluster events |
k8s_get_resource_yaml | Runs immediately — exports resource YAML |
k8s_apply_manifest | Pauses for approval — creates or updates resources |
k8s_delete_resource | Pauses for approval — deletes resources |
k8s_patch_resource | Pauses for approval — modifies resources |
ask_user | Built-in on every agent — asks you questions anytime |
The key is requireApproval — any tool listed there will pause execution until you explicitly approve it. Read-only tools run freely; write operations need your sign-off.
Step 4 — Open the UI#
Port-forward the kagent dashboard:
kubectl port-forward -n kagent svc/kagent-ui 8080:8080
Open http://localhost:8080 in your browser. You should see the kagent UI with your hitl-agent listed.
Step 5 — Try It Out#
Now for the fun part. Run through these tests to see human-in-the-loop in action.
Test 1: Read Without Approval#
- Select the hitl-agent in the UI
- Type:
List all pods in the kagent namespace - The agent calls
k8s_get_resources— it runs immediately with no approval prompt - You see the pod listing right away
This shows that read-only tools are not gated.
Test 2: Approve a Create#
- Type:
Create a ConfigMap called test-config in the default namespace with the key message set to "hello from kagent" - The agent calls
k8s_apply_manifest— execution pauses - You'll see Approve / Reject buttons appear, along with the YAML it wants to apply
- Click Approve
- The agent creates the ConfigMap and confirms
Test 3: Reject a Delete#
- Type:
Delete the ConfigMap test-config in the default namespace - The agent calls
k8s_delete_resource— execution pauses - Click Reject and enter a reason:
I want to keep this ConfigMap for now - The agent sees your reason and responds accordingly — it doesn't delete anything
Test 4: Agent Asks You a Question#
- Type:
Set up a namespace for my application - The request is vague, so the agent calls
ask_userto clarify (e.g., "What should the namespace be called?") - Answer the question and the agent continues with your input
How It Works#
The flow is straightforward:
You send a message
-> Agent decides which tool to call
-> Is the tool in requireApproval?
-> YES: Execution pauses, you see Approve/Reject in the UI
-> Approve: tool runs normally
-> Reject: agent receives your reason and adapts
-> NO: Tool runs immediately
The ask_user tool works the same way — the agent pauses, you answer, and it continues. Both use the same underlying confirmation mechanism, which keeps things simple.
Cleanup#
Delete the cluster when you're done:
kind delete cluster --name kagent-hitl
Key Takeaways#
requireApprovalis all you need — list the tools that need human sign-off- Read-only tools run freely, write operations pause for approval
ask_useris built-in on every agent — no extra config required- Rejection reasons are sent back to the LLM so it can adjust its approach