在过去的一年里,我一直是构建 Gordon 团队的一员——Gordon 是 Docker 的 AI 智能体。如果你最近使用过 Docker Desktop,应该见过它:点击侧边栏中的 Gordon 图标,或在终端运行 docker ai,你就会得到一个真正理解你的容器、镜像和环境的智能体。它不只是回答问题——它会采取行动。
但要构建一个让数百万开发者信任地处理其代码、容器、镜像、Compose 文件、构建和 CI 流水线的 AI 智能体,绝非易事。这是我们构建它的故事——我们做的决策、走过的弯路,以及一路上的所学所得。
Gordon v1#
Gordon 的第一个版本,是在如今大多数智能体工具诞生之前构建的。Gordon 从一开始就为 Docker 的 AI 体验提供支持——在 docs.docker.com、技术支持,以及 Docker Desktop 内部。我们使用 LangGraph 编写了最初的智能体循环,在 Docker 文档之上搭建了一套 RAG 系统,让 Gordon 能够基于真实内容回答问题,并构建了我们称之为"recipes"(配方)的机制——用于处理特定任务(如生成 Dockerfile 或调试容器)的确定性代码路径。可以把 recipes 理解为 MCP 和工具调用的前身,但完全是自定义的。每个 recipe 都是一套精心设计的流程:检测用户意图、收集合适的上下文,然后执行一系列我们验证过的操作步骤。
它上线了。有人使用它。我们也从中学到了很多——用户真正需要什么、LLM 在哪里力不从心,以及当你试图覆盖每一个边界情况时,确定性流程有多么脆弱。那时我们使用的还是 GPT-4o 时代的模型——能力不俗,但与现在可用的模型相比相差甚远。即便是今天最小的模型,比如 Haiku,也已经超越了我们当时拥有的能力,而这一切不过发生在不到一年内。
然而,当 Gordon v1 还在生产环境运行时,周遭的世界飞速发展。MCP 成为工具集成的标准。工具调用原生集成进了每个主流模型。智能体框架日趋成熟。模型本身也实现了质的飞跃。我们所构建的与现在所能实现的之间,差距越来越大。
于是我们决定从头重建 Gordon,将从 v1 中汲取的一切经验,与自发布以来涌现的新标准和新基础设施相结合。
基础架构#
重建的第一个问题是基础性的:我们在什么之上构建?
我们本可以在 LangGraph 技术栈上继续迭代,但我们一直在将 docker-agent(最初称为 cagent)构建为开源的 AI 智能体运行时,用自己的产品来支撑自己的工作,这是顺理成章的选择。
docker-agent 提供了一种以 YAML 声明式定义智能体的方式——包括模型、指令、工具,以及它们如何协作。你无需编写命令式代码来管理对话循环、工具调度和上下文窗口,只需描述智能体应该做什么,让运行时处理其余的事情。它几乎完全用 Go 编写,作为 Docker CLI 插件分发——docker agent run、docker agent new 等——与 Docker 工作流浑然一体。
对于 Gordon 来说,这意味着我们可以快速迭代。修改系统提示词?编辑 YAML 文件。添加新工具?加入工具集。更换模型?改一行。无需重新部署自定义代码,无需重建流水线。智能体定义本身就是产品。
构建于 docker-agent 之上的最大优势之一是分发能力。智能体定义被打包并以 OCI 制品的形式共享——与 Docker 用于容器镜像的格式相同。这意味着我们可以通过 docker agent push 将 Gordon 的新版本推送到 Docker Hub,并在另一端通过 docker agent pull 拉取。更新即时生效,无需重新构建任何代码,因为智能体循环并不内嵌在应用程序中——它存在于运行时中。Gordon 的定义只是 Docker Hub 上的一个 YAML 文件。当我们推送新版本时,每个 Docker Desktop 安装都会获取它(实际过程比这复杂一些,但大意如此)。无需二进制更新,无需应用商店审核,无需迁移脚本。运行时(docker-agent)与智能体定义(YAML 文件)之间的这种分离,是整个系统能够规模化运转的关键所在。
docker-agent 内置了多种工具集——用于读写文件的 filesystem、用于命令执行的 shell、用于推理草稿的 think、用于任务追踪的 todo,以及用于会话间持久化的 memory。除此之外,任何 MCP 服务器都可以作为工具接入。你还可以直接在 YAML 中定义自定义脚本工具——将 shell 脚本或 API 端点包装起来,以带类型参数的形式暴露给智能体。
使用 docker-agent 还意味着 Gordon 能够从运行时开箱即用的一切中受益——多提供商支持(OpenAI、Anthropic、Gemini、Bedrock、Mistral,甚至通过 Docker Model Runner 支持本地模型)、MCP 集成、内置多种检索策略的 RAG、支持子智能体和任务交接的多智能体编排,以及基于 OCI 的分发。当我们改进 docker-agent 时,Gordon 也随之变得更好。而当 Gordon 突破 docker-agent 的极限时,我们也让运行时对所有人都更加强大。
我们用 docker-agent 来构建 docker agent。这不是一句口号——这是我们实际的工作方式。
理解用户真正想要什么#
构建一个 AI 智能体很容易。构建一个真正有用的则很难。区别在于是否真正理解用户的需求。
早期,我们花了大量时间分析人们与 Docker 的交互方式。他们在论坛上提什么问题?他们在文档中搜索什么?他们在哪里遇到了困难?由于 Gordon v1 已经在 docs.docker.com、技术支持以及 Docker Desktop 内部的 AI 助手中运行,我们拥有两个无价的数据来源:文档和支持交互记录,以及来自 v1 会话的真实用户意图数据——人们让 Gordon 做什么、哪些 recipe 被触发了、哪里成功了、哪里不足。
规律显而易见:
- “为什么我的容器无法启动?” —— 调试是第一大使用场景。退出码、日志错误、网络问题、权限问题。
- “如何将我的应用容器化?” —— 用户有一个应用,想要一个好的 Dockerfile。不是教程里的通用模板——而是能理解他们项目结构的那种。
- “如何用 Docker 做 X?” —— 如果你知道命令,这些操作很简单;如果不知道,就得去翻文档。
这三个类别塑造了一切。Gordon 不是一个碰巧了解 Docker 的通用聊天机器人。它是专为这些工作流程而设计的智能体——调试、构建和管理。每一个工具、每一处提示词、每一个 UX 决策都源于此。
我们还发现,用户不会提出措辞规范的问题。他们会粘贴错误信息。他们描述症状,而非原因。他们提供不完整的上下文。一个好的智能体不能只靠关键词模式匹配——它需要理解意图,在必要时提出澄清性问题,并主动去调查。
构建智能体#
有了 docker-agent 作为运行时,以及对用户需求的清晰认识,我们开始动手构建。接下来是数周的快速迭代——智能体在这个过程中发生了巨大的变化。Gordon 以 OCI 制品的形式分发在 Docker Hub 上(docker/gordon),你实际上可以用 cagent pull docker/gordon:<tag> 拉取任意版本并查看完整的智能体定义。演进历程就在版本历史中一览无遗。
从多智能体集群到单一智能体#
我们对 Gordon v2 的第一次尝试颇为雄心勃勃。我们设计了一个包含九个专业子智能体的多智能体架构:Docker 专家、编程专家、部署专家、Kubernetes 专家、网络智能体、安全智能体、GitHub 集成智能体、DHI 迁移专家,甚至还有一个 Notion 智能体。根智能体充当协调者——分析用户请求,委派给合适的专家,并协调各方的响应。共享的待办事项列表在智能体之间传递上下文。
理论上很优雅。实践中却缓慢且不可预测。委派带来了延迟。协调者有时会选错子智能体。上下文在交接中丢失。而且智能体越多,就越难推断整个系统的行为。
于是我们将其合并。我们将几乎所有内容整合到一个拥有精心设计系统提示词的根智能体中。唯一保留下来的子智能体是 DHI 迁移专家,因为那套工作流程确实独特到足以拥有自己的智能体、工具和指令。其他所有内容——Docker 操作、调试、容器化、通用开发帮助——都住在根智能体中。
结果更快、更可预测,也更易于迭代。一个智能体、一套提示词、一个出问题时的排查入口。
模型选择#
模型的选择也发生了转变。v2 的早期版本运行在 Claude Sonnet 4.5 上——一个强大的模型,但在 Gordon 的运营规模下成本高昂。随着我们对提示词和工具的不断打磨,我们发现可以在 Claude Haiku 4.5——一个更小、更快、成本更低的模型——上获得同等质量。诀窍在于投入更好的提示词。每次我们改进指令——更具体的工具描述、更清晰的行为规则、更好的示例——Sonnet 和 Haiku 之间的差距就会缩小,直到在我们的使用场景中消失。
Gordon 目前在大多数交互中运行在 Haiku 4.5 上。速度差异显而易见——响应感觉更敏捷,工具调用解析更快,每次对话的成本也大幅下降。docker-agent 的多提供商支持意味着我们只需在 YAML 中改一行就能切换模型,所以我们一直在测试新出的模型。
提示词工程即产品开发#
最大的意外是:产品有多少内容存在于系统提示词中。Gordon 的提示词不是一段简短的说明——它是一份详尽的规范,涵盖身份认同、沟通风格、文件访问模式、知识库使用、响应长度控制、Dockerfile 最佳实践、调试工作流程和安全规则。
以下是 Gordon 定义的实际结构:
version: "2"
models:
brain:
provider: anthropic
model: claude-haiku-4-5-20251001
agents:
root:
model: brain
description: Gordon - Docker Agent
instruction: |
You are Gordon, an AI assistant created by Docker Inc.,
specialized in Docker and Docker-related products, tools,
and technologies...
sub_agents:
- DHI migration
toolsets:
- type: api
api_config:
name: knowledge_base
endpoint: https://ai-backend-service.docker.com/docs
...
- type: filesystem
- type: shell
- type: fetch
- type: todo
DHI migration:
model: brain
description: Migrates a Dockerfile to use Docker Hardened Images
toolsets:
- type: api
api_config:
name: get_image_tags
endpoint: https://ai-backend-service.docker.com/dhi
...
- type: filesystem
- type: shell我们不断地迭代提示词。每次发现一个失效模式——Gordon 太啰嗦、选错工具、问了不必要的澄清问题、使用了无意义的填充词——我们就会针对性地补充内容——一个新的评估用例,一条新的指令,等等。提示词从真实的用户交互和评估失败中有机地生长出来。它不是漂亮的代码,但它有效。还有一点:我们实际上已经不再手动编写这些提示词的大部分内容了。等聊完评估体系,我们再展开这一点。
用户体验#
AI 智能体的用户体验与聊天机器人有本质的不同。聊天机器人给你文字。智能体想要做事——运行命令、编辑文件、创建配置。这从根本上改变了你设计交互的方式。
我们最终确立的核心原则:先展示,再执行。
Gordon 在执行任何操作之前,都会先向你展示它打算做什么。要运行 shell 命令?你会看到命令内容。要编辑 Dockerfile?你会看到差异对比。要停止一个容器?你会看到是哪个。每一个操作都需要你明确批准。
这不仅仅是一项安全功能——它是建立信任的机制。当你初次使用 Gordon 时,你会批准每一个操作。随着时间推移,你开始信任它,因为你亲眼看到它做出了正确的决策。你批准得更快,不是因为你变得不那么谨慎,而是因为你对它的行为建立了信心。
我们也让 Gordon 在两个地方可用:Docker Desktop(图形界面)和 CLI(docker ai)。Desktop 体验是可视化的——你在对话旁边看到容器、镜像和日志。CLI 体验是为那些在终端生活并希望留在终端的开发者准备的。同一个智能体,同样的能力,不同的上下文。
我们刻意回避了一件事:自主模式。Gordon 不会在你不注意的时候自行完成十件事。它是一个协作型智能体——与你共同工作,而不是取代你工作。在一个开发者对 AI 工具在无人监督的情况下修改其基础设施持合理怀疑态度的世界里,这一点至关重要。
工具:Gordon 真正能做什么#
没有工具的 LLM 只是一个文本生成器。让 Gordon 成为智能体的,是它采取行动的能力。而把工具做对,是这个项目中最难的部分之一。
Gordon 的架构是客户端-服务器分离的。后端运行在 Docker 的服务器上,而客户端是一个与 Docker Desktop 捆绑的 CLI,运行在用户的机器上。客户端负责本地访问——读取文件、运行命令、与 Docker 守护进程交互——而后端负责 LLM 编排。当你通过 Docker Desktop 使用 Gordon 时,用户可以选择一个工作目录来限定其访问范围——或者使用默认目录。当你从终端使用 docker ai 时,它会在你当前所在的目录中工作。
Gordon 的核心工具集出人意料地精简:
- Filesystem —— 在用户的工作目录中读取、写入、编辑和列出文件。Gordon 通过它来检查你的项目结构、读取 Dockerfile、编写新配置。
- Shell —— 在用户终端执行命令(需要批准)。这是最核心的工具——通过 shell,Gordon 可以运行
docker build、docker compose up、docker scout、kubectl、git,以及用户机器上可用的任何其他工具。我们没有为每个 Docker 命令构建专用集成,而是给了智能体 shell 访问权限,让它使用开发者已经安装好的 CLI 工具。 - Fetch —— 向外部 URL 发起 HTTP 请求,获取文档、API 参考或 Gordon 回答问题所需的任何网络内容。
- Todo —— 追踪多步骤任务,让 Gordon 能够分解复杂请求并有条不紊地逐步完成。
- Knowledge base —— 一个自定义 API 工具,用于查询 Docker 的文档后端。我们从 v1 开始就在 Docker 文档之上构建了自己的 RAG 流水线,它不仅为 Gordon 提供支持,也为文档助手和技术支持提供支持。Gordon 通过这套共享基础设施获得最新的文档、最佳实践和常见模式。
- DHI migration —— 一个专用子智能体,拥有自己的工具集,用于将 Dockerfile 迁移到 Docker Hardened Images,包括一个用于解析 DHI 兼容镜像标签的 API 工具。
Gordon 流水线的第一步——理解用户想要什么并确定使用哪个工具——是通过 LLM 的工具调用完成的。这听起来简单,但它是我们花费最多时间进行实验的领域之一。
我们的发现:
工具描述比你想象的更重要。 一句模糊的简介远远不够。LLM 需要详细的描述以及何时使用每个工具的示例。我们发现更具描述性的工具定义能显著提升工具选择的准确性。
添加工具可能会破坏现有的东西。 这违反直觉。我们添加一个新工具,LLM 突然就不再正确触发已有工具了。工具集不只是一个列表——它是一个决策空间,扩展它会改变模型推理选择哪个工具的方式。
不同模型的行为各异。 工具调用在各提供商之间并没有标准化。对一个模型有效的方法可能对另一个失效。对 GPT-4 最优的描述可能会让 Claude 困惑,反之亦然。我们必须跨提供商进行测试,有时还需要针对特定模型定制描述。
充分利用现有的知识基础设施。 我们在 v1 中就在 Docker 文档之上构建了自己的 RAG 流水线,此后它一直为文档助手、技术支持和 Gordon 提供支持。对于 v2,我们不需要重新发明这一切——只需通过 API 工具将 Gordon 接入同一后端即可。多年积累并在生产环境中经过实战检验的索引文档,只需一个 API 调用即可获取。
评估体系#
关于 AI 智能体有一点必须承认:它们以微妙的方式出错。聊天机器人给出略微错误的答案令人恼火。智能体运行了错误的命令则是危险的。评估(evals)不是可选项——而是必需品。
docker-agent 内置了评估功能。工作流从录制会话开始——你正常与 Gordon 交互,当某段对话代表了一个好的测试用例时,你将其保存为评估。每个评估是一个 JSON 文件,记录了用户消息、预期的工具调用以及评估标准:响应必须满足的相关性陈述、预期的响应长度、应该调用哪些工具、应该创建什么文件。这些评估在 Docker 容器中运行以保证隔离——每个评估都获得一个干净的环境,因此结果是可复现的。
docker agent eval 运行完整的评估套件,从多个维度打分——工具调用准确性(Gordon 是否调用了正确的工具?)、相关性(响应是否真正解决了问题?)、响应长度,以及子智能体交接。LLM 评判者评估相关性标准,因此我们可以测试细微的行为,而不仅仅是字符串匹配。
这是我们发现回归问题的方式。Gordon 的每一次变更——提示词更新、工具更改、模型替换——在发布前都要经过完整的评估套件验证。在智能体系统中,一切都是相互关联的。对工具描述的一处微小调整可能会引发意想不到的连锁反应。当新版本模型发布时,我们在切换之前先运行评估套件。我们不盲目升级。
一个惨痛的教训:评估覆盖度比评估数量更重要。早期,我们的评估没有覆盖主要使用场景——我们在为边界情况优化 Gordon,而核心工作流(调试容器、生成 Dockerfile、回答 Docker 问题)的覆盖却不充分。我们在提升评估分数,却没有真正让 Gordon 对大多数用户更有用。一旦我们根据 v1 的真实使用模式重新平衡了评估套件,改进才开始真正反映用户的实际体验。
用智能体改进智能体#
还记得关于不再手动编写提示词的那个预告?实践中是这样运作的。
我们构建了一个自定义智能体——运行在更强大的模型(如 Claude Opus 4.6)上——其职责就是改进 Gordon 的系统提示词。工作流程:给它 Gordon 当前的智能体定义、一组失败的评估用例,以及评估结果。该智能体分析失败原因,提出提示词修改建议,并输出更新后的 YAML。我们针对新版本运行评估套件。如果分数提升且没有出现回归,我们就发布它。
这创造了一个紧密的改进循环。用户反馈 Gordon 总是提出过多澄清问题,而不是直接读取文件?我们为此添加一个评估用例,把失败指向优化器智能体,让它找出正确的提示词修改方案。它可能会添加这样一条规则:“始终使用 filesystem 工具直接读取文件。永远不要要求用户粘贴内容。"——这正是区分一个好的智能体和一个令人沮丧的智能体的那种具体、可执行的指令。
使用更强大的模型作为"老师"来改进"学生”,这是刻意为之的。Opus 拥有足够的推理能力来理解细微的行为问题,并精确地制定指令,引导 Haiku 朝正确方向发展。一路向下,全是智能体。
Gordon 提示词中的大多数详细行为规则——被禁止的填充词、文件访问模式、响应长度指南、调试序列——都是由优化器智能体编写或完善的,而不是人工编写的。我们设定方向,并通过评估定义什么是好的。智能体自己找出如何实现它。
下一步:记忆#
目前,Gordon 在会话之间是无状态的。每次对话都从头开始。你关闭 Docker Desktop,Gordon 就忘记了一切——你的项目结构、你一直在调试的问题、你偏好的 Dockerfile 模式。
记忆是下一个前沿领域。我们正在努力赋予 Gordon 跨会话记住上下文的能力:
- 项目上下文 —— Gordon 应该记住你的项目结构、你的 Docker 配置,以及你使用的模式
- 交互历史 —— 如果你上周解决了一个问题,当类似的问题再次出现时,Gordon 应该知晓
- 用户偏好 —— 如果你总是使用多阶段构建,Gordon 应该默认建议使用它们
这比听起来要难。AI 智能体中的记忆不只是"保存聊天历史"。它关乎决定什么值得记住、如何高效检索,以及如何防止其过时。一个浮现出无关上下文的记忆系统,还不如没有记忆。
docker-agent 已经具备了相应的构建模块。有一个 memory 工具集,可以在会话间将信息持久化到本地数据库中——智能体可以在工作时存储和检索事实。构件已经就绪。挑战在于让它感觉自然——Gordon 应该在没有提示的情况下主动呈现相关记忆,在没有明确告知的情况下学习你的偏好,并遗忘不再相关的内容。就像我在构建 Eva 时使用的滑动窗口,但更智能。
目标很简单:Gordon 应该像一个了解你项目的队友,而不是一个每次都要从头解释的陌生人。
构建 Gordon 是我参与过的最具挑战性也最令人充实的项目之一。AI 智能体仍处于早期阶段——工具在快速演进,最佳实践仍在书写,用户期望随着每一次新模型发布而不断变化。但核心洞察始终不变:开发者不需要另一个聊天机器人。他们需要一个理解其环境、采取行动、并通过每一次被批准的命令赢得信任的智能体。
如果你想试用 Gordon,请更新到最新版的 Docker Desktop,在侧边栏找到 Gordon 图标,或从终端运行 docker ai。
如果你想构建自己的智能体,欢迎了解 docker-agent——它是开源的,也是 Gordon 运行其上的同一运行时。







