ITPub博客

首页 > 人工智能 > 人工智能 > 从零到智能体构建器:在你的笔记本电脑上运行 kagent

从零到智能体构建器:在你的笔记本电脑上运行 kagent

人工智能 作者:ITPUB社群 时间:1970-01-01 08:33:46 0 删除 编辑

发布于 2026 年 10 月 12 日,作者:Antonio Berben(Solo.io)和 Felipe Vicens(Telefónica)

本文中重点介绍的 CNCF 项目

如果你看过无代码智能体构建器的演示,你就知道那个瞬间。有人输入一句话,等一分钟,然后它就在那里了:一个新的智能体,正在运行,带着工具。这很惊艳。大约十秒钟。

然后问题就来了。它在哪里运行?它能触碰什么?谁批准了它?

这篇文章讲的是第一部分,那令人印象深刻的十秒钟。我想说明这并非魔法,你可以自己构建它,在一个下午之内,用开源技术,花费最少的 token。这个工具是 kagent,一个 CNCF Sandbox 项目,它将 AI 智能体作为 Kubernetes(容器编排平台) 资源来运行。那些问题会在最后回来,因为它们才是真正的工作。

为什么 kagent 让这件事几乎变得无聊

在 kagent 中,智能体不是你手动启动的进程。它是一个自定义资源。你编写一个 Agent(智能体),包含系统提示词、模型和工具列表。你应用它,控制器就会把它变成一个运行中的 pod,使用 A2A 通信。

一旦智能体只是 YAML,那么一个构建智能体的智能体就只是一个能编写某种 YAML 的智能体。不需要新框架,不需要插件。一个 Agent,三个工具,以及一个知道 Agent 长什么样的提示词。

图 1:一个构建智能体的智能体,运行在 kagent 上。

开始之前:这个实验不是什么

这里的目标是构建器。安全不在范围内,这是故意的,我想坦率地说明这意味着什么:

  • kagent 运行在其默认的不安全认证模式下,因此每个请求都以同一用户身份执行。
  • Chart 自带的 PostgreSQL(开源关系型数据库) 使用其内置密码。
  • UI 和 API(应用程序编程接口) 只能通过 kubectl port-forward 在 127.0.0.1 上访问。

这对笔记本电脑来说没问题。但对共享集群来说就不行了,我稍后会回到为什么这个差距很重要。

你需要 Docker(容器化平台)、kind、kubectl、helm、jq 和 Ollama(或者,你可以使用 Claude、OpenAI 或任何其他提供商的 API Key,但我们希望尽可能降低成本)。无需克隆任何内容:你边做边创建每个文件。从一个空文件夹开始:

mkdir kagent-agent-builder && cd kagent-agent-builder

步骤 1:选择一个真正能胜任的本地模型

这是让我感到惊讶的部分。通过工具调用编写有效的 Kubernetes 清单比聊天更难,小型模型会暴露这一点。在我的运行中,qwen2.5:7b-instruct 发明了自己的工具块格式,而 qwen2.5:14b-instruct 有时会礼貌地回答却从未调用 apply 工具。qwen3-coder:30b-a3b 在每次运行中都做对了,所以这就是本文使用的模型:

ollama pull qwen3-coder:30b-a3b-q4_K_M   # about 18 GB on disk

集群通过 host.docker.internal:11434 访问 Ollama,这在 Docker Desktop 上开箱即用。在 Linux 上,你必须将下面 values 文件中的该主机改为 kind 节点可以访问的地址。我尚未测试该路径。

如果你更喜欢托管模型,kagent 的 Helm chart 也有 OpenAI、Anthropic、Azure OpenAI 和 Gemini 的提供商配置块。当然,那样就不再免费了。

步骤 2:安装 kagent,并使用一个只能写入 Agents 的工具服务器

kind create cluster --name kagent-builder

kagent 附带一个带有 Kubernetes 工具的工具服务器,默认情况下该 chart 为其 ServiceAccount 授予 cluster-admin 权限。即使是在笔记本电脑上,由 cluster-admin 支持的构建器也是个坏主意,因此 values 文件改变了这一点。创建它:

providers:
  default: ollama
  ollama:
    provider: Ollama
    model: "qwen3-coder:30b-a3b-q4_K_M"
    config:
      host: host.docker.internal:11434
      options:
        num_ctx: "32768"
kagent-tools:
  tools:
    enabledTools:
      - k8s
  rbac:
    readOnly: true
    additionalRules:
      - apiGroups: ["kagent.dev"]
        resources: ["agents"]
        verbs: ["get", "list", "watch", "create", "update", "patch"]
grafana-mcp: {enabled: false}
k8s-agent: {enabled: false}
kgateway-agent: {enabled: false}
istio-agent: {enabled: false}
promql-agent: {enabled: false}
observability-agent: {enabled: false}
argo-rollouts-agent: {enabled: false}
helm-agent: {enabled: false}
cilium-policy-agent: {enabled: false}
cilium-manager-agent: {enabled: false}
cilium-debug-agent: {enabled: false}

该模型成为默认的 ModelConfig。工具服务器只加载其 Kubernetes 工具(共 22 个)。它获得一个只读角色外加一条额外规则:它可以写入 agents.kagent.dev,仅此而已。最后一块关闭了 chart 默认安装的示例 agents,因此 UI 只显示你的。

使用该文件安装 CRD 和 kagent 0.10.2,然后等待 Pod 就绪:

helm upgrade --install kagent-crds oci://ghcr.io/kagent-dev/kagent/helm/kagent-crds \
  --kube-context kind-kagent-builder \
  --version 0.10.2 --namespace kagent --create-namespace
helm upgrade --install kagent oci://ghcr.io/kagent-dev/kagent/helm/kagent \
  --kube-context kind-kagent-builder \
  --version 0.10.2 --namespace kagent --values kagent-values.yaml
for d in kagent-controller kagent-ui kagent-tools; do
  kubectl --context=kind-kagent-builder -n kagent rollout status deploy/$d --timeout=300s
done

步骤 3:构建器只是一个 agent

这就是整个构建器。创建该文件:

apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
  name: agent-builder
  namespace: kagent
spec:
  description: Builds new kagent agents from a plain-language description and deploys them.
  type: Declarative
  declarative:
    modelConfig: default-model-config
    stream: false
    systemMessage: |
      You build kagent agents. The user describes, in plain language, an agent
      they want. You turn that into ONE Kubernetes manifest and apply it. Do not
      ask follow-up questions: pick sensible defaults and go.
      How you work, in this order:
      1. List the existing agents with k8s_get_resources (resource_type
         agents.kagent.dev, namespace kagent). Pick a name that is not taken;
         never overwrite an existing agent.
      2. Write the manifest in exactly the shape below and apply it with
         k8s_apply_manifest. The user confirms the apply in the UI; that is
         expected.
      3. Read it back with k8s_get_resource_yaml (resource_type
         agents.kagent.dev, namespace kagent) and look at the Accepted and
         Ready conditions in its status. Ready can take a few seconds.
      The manifest must be exactly this shape, and nothing else:
        apiVersion: kagent.dev/v1alpha2
        kind: Agent
        metadata:
          name: <short-kebab-case-name>
          namespace: kagent
          labels:
            app.kubernetes.io/managed-by: agent-builder
        spec:
          description: |-
            <one sentence>
          type: Declarative
          declarative:
            modelConfig: default-model-config
            stream: false
            systemMessage: |
              <a clear prompt for the new agent, written for its task>
            tools:
              - type: McpServer
                mcpServer:
                  apiGroup: kagent.dev
                  kind: RemoteMCPServer
                  name: kagent-tool-server
                  toolNames: [<pick from the read-only list below>]
      Read-only tools you may give it: k8s_get_resources, k8s_get_resource_yaml,
      k8s_describe_resource, k8s_get_events, k8s_get_pod_logs.
      Never another kind, never another namespace, never write or delete tools,
      never more than one document.
      YAML that does not break: free text always goes in a block scalar, as in
      the shape above (description with |-, systemMessage with |). In a plain
      scalar, a colon followed by a space makes the YAML invalid, and a space
      followed by # silently cuts the value short.
      When something goes wrong:
      - If the user rejects the apply, do not retry it and do not read the
        agent back. Say it was not applied, and stop.
      - Never create a second resource with another name (-test, -check, -v2)
        to find out what failed. It tells you nothing and leaves clutter.
      - Do not name a cause you have not checked. An opaque error is not proof
        of a name clash, a permission problem or a network issue.
      - Report the error exactly as the tool returned it, and stop.
      When it is done, answer in three short lines: the agent's name, what it
      does, and which tools it can use. Then one more line with its real state
      from step 3 (Accepted, Ready). Never claim it is ready if it is not.
    tools:
      - type: McpServer
        mcpServer:
          apiGroup: kagent.dev
          kind: RemoteMCPServer
          name: kagent-tool-server
          toolNames:
            - k8s_apply_manifest
            - k8s_get_resources
            - k8s_get_resource_yaml
          requireApproval:
            - k8s_apply_manifest
    a2aConfig:
      skills:
        - id: build-agent
          name: Build an agent
          description: Turns a plain-language description into a deployed kagent agent.
          tags: [builder, agents]
          examples:
            - "Create an agent for the support team that answers questions about the state of the services in the cluster"

应用它并等待 kagent 报告其状态为 Ready:

kubectl --context=kind-kagent-builder apply -f agent-builder.yaml
kubectl --context=kind-kagent-builder -n kagent wait --for=condition=Ready \
  agent/agent-builder --timeout=300s

末尾的 tools 块是构建器获得其限制的地方。toolNames 是一个允许列表:在服务器上的 22 个工具中,模型只能看到三个。requireApproval 是 kagent 的人在回路开关。当智能体调用该列表中的工具时,运行会暂停,用户必须批准或拒绝该调用。

系统提示词完成其余工作。它告诉模型首先列出已有的智能体,这样它就不会覆盖任何一个,然后给出它可以生成的精确清单格式:一个 Agent,位于 kagent 命名空间中,带有一个标记其由构建器构建的标签,并且只使用只读工具。它还包含了我通过艰难方式学到的一些经验。自由文本放在 YAML 块标量中,因为普通字符串中的冒号加空格会破坏清单。当某些操作失败时,模型必须报告错误并停止,而不是创建 my-agent-v2 来看看会发生什么。

请记住,提示词是一个请求,而不是保证。保证来自允许列表、审批和 RBAC。

步骤 4:请求一个智能体

kubectl --context=kind-kagent-builder -n kagent port-forward \
  --address 127.0.0.1 svc/kagent-ui 8080:8080

转到 http://localhost:8080,选择 agent-builder 并提出请求:

Create an agent for the support team that answers questions
about the state of the services in the cluster

大约半分钟后,运行停止。UI 显示模型想要应用的清单以及两个按钮。

图 2:UI 显示在应用前需要用户批准的清单。

在点击之前先阅读它。这是人类能够确切看到即将进入集群的内容的唯一时刻。批准后,构建器会应用它、读回它并报告真实状态:

图 3:UI 显示应用清单后的结果和真实状态。

新智能体会出现在智能体列表中,你可以立即与它聊天。问它 kagent 命名空间中存在哪些服务。它会使用只读工具回答,因为这是构建器被允许授予它的唯一工具。

步骤 5:不使用 UI 的相同操作

每个 kagent 智能体都是控制器上的一个 A2A 端点,因此你可以编写脚本实现整个流程。转发控制器端口并发送请求:

kubectl --context=kind-kagent-builder -n kagent port-forward \
  --address 127.0.0.1 svc/kagent-controller 8083:8083 &
sleep 3   # give the port-forward a moment to listen

curl -s http://127.0.0.1:8083/api/a2a/kagent/agent-builder/ \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":"1","method":"message/send","params":{"message":{"role":"user","messageId":"1","parts":[{"kind":"text","text":"Create an agent that lists the pods that are not running in the kagent namespace"}]}}}' \
  > build.json

jq -r '.result.status.state' build.json    # input-required

input-required 表示 apply 正在等待你的响应。待处理的 manifest 位于状态消息的 data 部分中。要回复,请在同一 task 和 context 上发送一条带有决策的消息:

TASK=$(jq -r .result.id build.json); CTX=$(jq -r .result.contextId build.json)
curl -s http://127.0.0.1:8083/api/a2a/kagent/agent-builder/ \
  -H 'Content-Type: application/json' \
  -d "{\"jsonrpc\":\"2.0\",\"id\":\"2\",\"method\":\"message/send\",\"params\":{\"message\":{\"role\":\"user\",\"messageId\":\"2\",\"taskId\":\"$TASK\",\"contextId\":\"$CTX\",\"parts\":[{\"kind\":\"data\",\"data\":{\"decision_type\":\"approve\"}}]}}}" \
  | jq -r '.result.status.state'            # completed

使用 “reject” 代替 “approve”,则不会创建任何内容。无论哪种方式,集群都会告诉你实际发生了什么:

kubectl --context=kind-kagent-builder -n kagent get agents

为什么需要三道护栏而不是一道

图 4:三道不依赖于提示词的护栏。

人们很容易认为提示词就足够了。事实并非如此,而且不是因为模型有恶意。模型会写错 YAML,以各种创造性的方式重试,并误解请求。

  • 在为本文检查命令时,这种情况就发生在我身上。提示词中明确写明了工具服务器名称 kagent-tool-server,但有一次运行仍然生成了一个指向 kagent-tools 的 Agent。我的 curl 像脚本一样盲目地批准了它。控制器拒绝了它:Accepted=False, RemoteMCPServer "kagent-tools" not found。没有出问题,但这很好地提醒了我们真正的检查在哪里。每一层都会捕获上一层放过的内容:
  • 允许列表。如果模型试图调用 k8s_delete_resource,该工具根本不存在。
  • 审批。如果它写出了一个你意料之外的 manifest,你会在它存在之前就看到它。
  • RBAC。如果你批准了不该批准的内容,API 服务器仍然会拒绝任何不是 Agent 的资源。

你可以自己验证最后一项:

SA=system:serviceaccount:kagent:kagent-tools

kubectl --context=kind-kagent-builder auth can-i create
agents.kagent.dev --as=$SA -n kagent   # yes

kubectl --context=kind-kagent-builder auth can-i create
deployments       --as=$SA -n kagent   # no

kubectl --context=kind-kagent-builder auth can-i get secrets              
--as=$SA -n kagent   # no

本实验未涉及的部分

所以构建这个智能体只用了一个 YAML 文件和一两次 Helm 安装。这是简单的部分,而这正是我认为它不再是有趣部分的原因。

再看一下图 4 底部的方框。在这台笔记本电脑上,没有人知道是谁请求了这个智能体,因为所有人都是同一个用户。没有人知道它的模型调用花了多少钱,或者属于哪个团队。也没有人决定这个智能体是否应该存在,除了碰巧在聊天中的某个人点了一下。现在想象一家公司,拥有这样的构建器、几个框架、一个托管智能体平台以及开发者机器上的几个助手。每一个都以不同的方式回答这些问题,如果它们能回答的话。

这就是为什么需要一个治理层,它位于智能体的前面,无论它们来自哪里,而不是位于每个智能体内部。在开源世界中,一个选择是 agentgateway,这是一个 Linux 基金会项目。它代理 LLM、MCP 和 A2A 流量,支持身份验证(JWT、API 密钥)、使用 CEL 的细粒度授权,以及为每次调用提供 OpenTelemetry 指标、日志和追踪。将它接入本实验的前面是一个很好的下一步练习,可能也是我的下一篇文章。

清理

所有内容都存在于 kind 集群中,因此只需一条命令即可将其移除:

kind delete cluster --name kagent-builder

下一步方向

kagent 文档涵盖了更多工具服务器、其他模型提供商以及用代码编写的智能体,项目位于 github.com/kagent-dev/kagent。如果你在此基础上构建了某些东西,或者你找到了一个能很好处理构建器的更小的本地模型,我非常希望听到相关消息。


转载声明:本文为转载整理,原文版权归原作者及原站点所有。

原文来源:CNCF Blog

原创地址:https://www.cncf.io/blog/2026/10/12/from-zero-to-agent-builder-running-kagent-on-your-laptop/

原文作者:Antonio Berben(Solo.io) and Felipe Vicens(Telefóni;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2022-11-21

  • 博文量
    1199
  • 访问量
    549883