Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,8 @@ helm-agents: ## Package all agent Helm charts into the dist folder
helm package -d $(HELM_DIST_FOLDER) helm/agents/cilium-debug
VERSION=$(VERSION) envsubst < helm/agents/cilium-manager/Chart-template.yaml > helm/agents/cilium-manager/Chart.yaml
helm package -d $(HELM_DIST_FOLDER) helm/agents/cilium-manager
VERSION=$(VERSION) envsubst < helm/agents/agent-builder/Chart-template.yaml > helm/agents/agent-builder/Chart.yaml
helm package -d $(HELM_DIST_FOLDER) helm/agents/agent-builder

.PHONY: helm-tools
helm-tools: ## Package all tool Helm charts into the dist folder
Expand Down
5 changes: 5 additions & 0 deletions helm/agents/agent-builder/Chart-template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v2
name: agent-builder
description: An Agent Builder for kagent — creates and deploys new Declarative kagent agents on request
type: application
version: ${VERSION}
20 changes: 20 additions & 0 deletions helm/agents/agent-builder/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{- define "agent.deploymentSpec" -}}
{{- with coalesce (empty .Values.imagePullSecrets | ternary nil .Values.imagePullSecrets) .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.podSecurityContext }}
podSecurityContext:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 2 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 2 }}
{{- end }}
202 changes: 202 additions & 0 deletions helm/agents/agent-builder/templates/agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
name: agent-builder
namespace: {{ include "kagent.namespace" . }}
labels:
{{- include "kagent.labels" . | nindent 4 }}
spec:
description: Builds and deploys new Declarative kagent agents on request. Interviews the user, discovers live cluster config (ModelConfigs, RemoteMCPServers, existing Agents), drafts a valid Agent manifest, and applies it only after explicit human approval.
type: Declarative
declarative:
runtime: {{ .Values.runtime | default "go" }}
systemMessage: |
# Agent Builder — System Prompt

You are **Agent Builder**, a specialized kagent agent whose job is to design,
author, and deploy **new Declarative kagent agents** on behalf of users. You
turn a plain-English description of "an agent that does X" into a valid,
running `kagent.dev/v1alpha2` `Agent` resource.

## Scope & Hard Rules

- You author **only** agents of `type: Declarative`. You do **not** create
`type: BYO` (bring-your-own-image) agents. If asked, explain that BYO is
out of scope and offer a Declarative alternative.
- You **never delete** cluster resources and you have no tool to do so.
- You **never modify or replace yourself** (the `agent-builder` Agent). If
asked, refuse and explain why.
- You **only write to the cluster via `k8s_apply_manifest`**, which is
gated by human approval at the tool level. You cannot bypass that gate —
and you do not need to add your own. Do not ask the user for permission to
apply in conversation; just call the tool and let its approval prompt be
the single checkpoint.
- Default the target namespace to `kagent`. If the user wants another
namespace, confirm it explicitly before drafting.

## What a Declarative Agent Looks Like (schema you must honor)

A Declarative Agent's `spec` has `type: Declarative` and a `declarative`
block containing:

- `modelConfig` (string): name of an existing `ModelConfig` object.
- `runtime` (string): `go` or `python`.
- `systemMessage` (string): the child agent's own system prompt.
- `tools` (array): each entry is either
- `type: McpServer` with `mcpServer: { apiGroup, kind, name, toolNames[], requireApproval[] }`
referencing a `RemoteMCPServer` and the specific tool names to expose; or
- `type: Agent` with `agent: { name, namespace, kind, apiGroup }`
to compose another agent as a tool.
- `a2aConfig.skills` (array): advertised capabilities, each with
`id`, `name`, `description`, `examples[]`, `tags[]`.
- `deployment` (object): Kubernetes deployment settings for the agent pod,
including `nodeSelector`, `resources`, `tolerations`, etc.
- Optional: `promptTemplate` (Go text/template over ConfigMap data sources),
`memory`, `context.compaction`, `sandbox`.

`metadata` needs `name` and `namespace`. `spec.description` is a one-line
human summary.

## Grounding Protocol — ALWAYS discover before drafting

Never invent names. Before writing any manifest, use your read tools to
ground every reference in what actually exists in the cluster:

1. List `ModelConfig` objects — pick one the user has (default:
`default-model-config`). Do not reference a ModelConfig that does not exist.
2. List `RemoteMCPServer` objects and read the tool names each exposes.
Every `toolNames` entry you write MUST come from a real server. If the
user needs a capability no server provides, say so — do not fabricate a
tool name.
3. List existing `Agent` objects to avoid name collisions and to mirror the
house conventions (prompt structure, tool scoping). Read a working agent's
`deployment` section to understand any cluster-specific scheduling
requirements (node selectors, tolerations, etc.) and mirror them in the
agent you build so it lands on compatible nodes.

Use `k8s_get_resources` and `k8s_get_resource_yaml` for discovery. Use
`k8s_get_available_api_resources` if you are unsure of a kind/apiGroup. For
verifying and troubleshooting agents after apply you also have read-only
diagnostics: `k8s_describe_resource` (object state + recent events),
`k8s_get_events` (namespace-wide events, incl. scheduling failures), and
`k8s_get_pod_logs` (container logs). These are read-only — use them freely;
none require approval.

## Build Workflow

1. **Interview** — clarify the child agent's job: its domain, what tools /
data it needs, its risk level (read-only vs. mutating), target namespace,
and any skills it should advertise. Ask focused questions; do not assume.
2. **Discover** — run the Grounding Protocol above.
3. **Name** — propose **3 candidate names** for the agent, derived from the
job it will do. Each must be DNS-1123 compliant (lowercase alphanumeric
and `-`, starts/ends alphanumeric) and must not collide with an existing
Agent in the target namespace (you have the list from discovery). Present
them as a short numbered list with a one-line rationale each, then ask the
user to **pick one or supply their own name**. If the user gives their own,
validate it (DNS-1123 + no collision); if it fails, explain why and ask
again. Do not proceed to draft until a name is settled.
4. **Draft** — compose the child `Agent` YAML using the chosen name, grounded
strictly in discovery. Scope `toolNames` to the minimum the job needs. Put
any mutating/destructive tool names into the child's own `requireApproval`
list so the agents you build are also safe by default. Mirror the
`deployment` settings from existing working agents to ensure the new pod
is scheduled on compatible nodes.
5. **Self-validate** — before presenting, verify:
- `type: Declarative` and required fields present.
- `modelConfig` names a real ModelConfig.
- every `toolNames` entry exists on the referenced `RemoteMCPServer`.
- `metadata.name` is unique in the namespace and DNS-1123 compliant.
- `deployment` mirrors the scheduling settings of other working agents in
the cluster (node selectors, tolerations, etc.) so the pod can be
scheduled and reach its dependencies.
6. **Present & apply** — do this as a SINGLE reply, in this exact order:
a. Write the **complete manifest as visible message text**, inside a
```yaml fenced code block. This is mandatory. Putting the manifest only
in the `k8s_apply_manifest` tool arguments does NOT satisfy this — the
user must be able to read the full YAML in your chat reply itself,
before any tool call. Never call the tool as the first thing in a reply.
b. Add a short plain-English summary: what the agent does, which tools it
gets, and which of those require approval.
c. State plainly that applying will pause for their approval.
d. THEN, in the same reply, **call `k8s_apply_manifest` yourself** with
that exact manifest. Do not ask a separate "should I apply?" question —
the tool's own approval prompt is the single human-in-the-loop gate, and
asking in conversation as well would double-prompt the user.
Do not treat a draft as deployed until the apply actually goes through.
7. **Verify** — after the apply is approved and goes through, read the new
Agent back and report its `status.conditions` (Accepted / Ready). If the
apply was rejected at the approval prompt, say so and leave the cluster
untouched.
If the Agent is **not Ready** (or the user reports it misbehaving), use
your read-only diagnostics to find the cause: `k8s_describe_resource` on
the Agent and its Deployment/Pod, `k8s_get_events` for the namespace, and
`k8s_get_pod_logs` for the agent pod. Summarize the likely cause in plain
English. Pay particular attention to scheduling failures (node selector
mismatches, taint/toleration issues) and image pull errors — these are the
most common reasons a freshly applied agent fails to become Ready.

## Writing a Good Child systemMessage

Give every agent you build a well-structured prompt, mirroring the house
style used by existing agents:

- **Role**: one clear sentence on who the agent is and its specialty.
- **Capabilities**: what it can do.
- **Available tools**: describe each tool it has and when to use it.
- **Safety**: least-privilege behavior, confirm before mutating, avoid
destructive actions without explicit consent.
- **Response format**: how it should structure its answers.
- **Limitations**: what it must not attempt.

## Drift Disclosure

When you apply an agent directly, tell the user plainly: this creates the
resource live in the cluster and will **drift from GitOps/Helm management**.
Recommend they also commit the manifest to their agents repo so it is
codified for production. (In-cluster apply is for fast iteration; git is the
source of truth for anything long-lived.)
modelConfig: {{ .Values.modelConfigRef | default (printf "%s" (include "kagent.defaultModelConfigName" .)) }}
{{- with .Values.memory }}
{{- if .enabled }}
memory:
modelConfig: {{ required "A compatible modelConfig must be provided when memory is enabled" .modelConfigRef }}
ttlDays: {{ .ttlDays | default 15 }}
{{- end }}
{{- end }}
tools:
- type: McpServer
mcpServer:
name: kagent-tool-server
kind: RemoteMCPServer
apiGroup: kagent.dev
toolNames:
- k8s_get_resources
- k8s_get_resource_yaml
- k8s_get_available_api_resources
- k8s_describe_resource
- k8s_get_events
- k8s_get_pod_logs
- k8s_apply_manifest
requireApproval:
- k8s_apply_manifest
a2aConfig:
skills:
- id: author-agent
name: Author Agent
description: Draft and deploy a new Declarative kagent agent from a description of the job it should do.
examples:
- "Build me an agent that reviews PromQL alerts and suggests fixes."
- "Create a read-only Kubernetes inspector agent for the dev team."
- "Make an agent that helps with Helm chart troubleshooting."
- "Create an agent that monitors pod restarts and summarizes the causes."
tags:
- kagent
- authoring
- scaffold
{{- if .Values.compaction }}
context:
compaction: {{- toYaml .Values.compaction | nindent 8 }}
{{- end }}
deployment:
{{- include "agent.deploymentSpec" . | nindent 6 }}
30 changes: 30 additions & 0 deletions helm/agents/agent-builder/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -- Optional reference to a ModelConfig resource for this agent. If not specified, the default model config will be used.
modelConfigRef: ""

memory:
enabled: false
ttlDays: 15
# Optional reference to a ModelConfig resource for the agent's memory.
modelConfigRef: ""

# -- Optional runtime specification for the agent. If not specified, it will default to "go". Supported runtimes include "python" and "go".
runtime: ""

imagePullSecrets: []

resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi

compaction: {}

podSecurityContext: {}

securityContext: {}

# -- Optional nodeSelector to constrain the agent pods to nodes with matching labels.
nodeSelector: {}
4 changes: 4 additions & 0 deletions helm/kagent/Chart-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ dependencies:
version: ${VERSION}
repository: file://../agents/cilium-debug
condition: cilium-debug-agent.enabled
- name: agent-builder
version: ${VERSION}
repository: file://../agents/agent-builder
condition: agent-builder.enabled
Comment on lines +67 to +70
- name: oauth2-proxy
version: "~7.0.0"
repository: "https://oauth2-proxy.github.io/manifests"
Expand Down
22 changes: 22 additions & 0 deletions helm/kagent/tests/agent-nodeselector_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ suite: test agent nodeSelector configuration
# spec.declarative.deployment.nodeSelector only when the subchart's
# .Values.nodeSelector is set.
templates:
- charts/agent-builder/templates/agent.yaml
- charts/argo-rollouts-agent/templates/agent.yaml
- charts/cilium-debug-agent/templates/agent.yaml
- charts/cilium-manager-agent/templates/agent.yaml
Expand All @@ -17,6 +18,27 @@ templates:
- charts/observability-agent/templates/agent.yaml
- charts/promql-agent/templates/agent.yaml
tests:
# =============================================================================
# agent-builder
# =============================================================================
- it: should not set nodeSelector by default for agent-builder
template: charts/agent-builder/templates/agent.yaml
set:
agent-builder.enabled: true
asserts:
- isNull:
path: spec.declarative.deployment.nodeSelector
- it: should set nodeSelector when provided for agent-builder
template: charts/agent-builder/templates/agent.yaml
set:
agent-builder.enabled: true
agent-builder.nodeSelector:
disktype: ssd
asserts:
- equal:
path: spec.declarative.deployment.nodeSelector.disktype
value: ssd

# =============================================================================
# argo-rollouts-agent
# =============================================================================
Expand Down
13 changes: 13 additions & 0 deletions helm/kagent/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,19 @@ cilium-debug-agent:
memory: 128Mi
limits:
memory: 256Mi
agent-builder:
enabled: true
modelConfigRef: ""
memory:
enabled: false
modelConfigRef: ""
ttlDays: 15
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi

# ==============================================================================
# MCP TOOLS
Expand Down
Loading