Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 }}
207 changes: 207 additions & 0 deletions helm/agents/agent-builder/templates/agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
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 (rough sketch — verify against the live CRD before drafting)

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. For the authoritative
field set, read the `agents.kagent.dev` CRD (`k8s_get_resource_yaml` on
kind `CustomResourceDefinition`, name `agents.kagent.dev`) — trust its
`openAPIV3Schema` over the rough sketch above.

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`.
- every `requireApproval` entry also appears in `toolNames` — this is
CEL-enforced on the CRD and any violation causes a guaranteed apply rejection.
- `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