将 AI 作为大规模工程系统的‘思考伙伴’

TL;DR · AI 摘要
AI 可作为工程领导者的‘思考伙伴’,通过五个角色帮助管理大规模工程系统的认知负荷。
核心要点
- AI 在工程系统中可扮演五种角色:考古学家、实验者、批评家、作者和审阅者。
- AI 能够合成遗留上下文,加速高层架构决策,并进行设计压力测试。
- Julie Qiu 是 Google Cloud CLI 和 SDK 的 Uber Tech Lead,负责构建多语言客户端库。
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- AI as a Thinking Partner in Engineering Systems
- Five Roles of AI
- Archaeologist
- Experimenter
- Critic
- Author
- Reviewer
- Use Cases
- Synthesizing Legacy Context
- Design Pressure Testing
- Accelerating Architectural Decisions
- Julie Qiu's Work
- Google Cloud CLI and SDK
- Go Security Team Leadership
金句 / Highlights
值得收藏与分享的关键句。
AI 提供了所需的‘RAM’来合成遗留上下文,对设计进行压力测试,并加速高层架构决策。
Julie Qiu 是 Google 的 Cloud SDK 的 Uber Tech Lead。
你看到的 Go 和 Python 代码是我们所说的客户端库。它们是围绕 API 的语言特定包装器。
标题:将AI作为大规模工程系统的思考伙伴
URL 来源:https://www.infoq.com/presentations/ai-large-scale-engineering-systems/
发布时间:2026-05-15T13:00:00+0000
Markdown 内容: [InfoQ 首页](https://www.infoq.com/ "InfoQ 首页")[演讲](https://www.infoq.com/presentations "演讲")将AI作为大规模工程系统的思考伙伴
观看演讲
速度:
下载
42:10
/presentations/ai-large-scale-engineering-systems/en/slides/Jul-1778849544183.jpg)
摘要
Julie Qiu 解释了AI如何作为工程负责人的“思考伙伴”。她讨论了五个不同的角色——考古学家、实验者、批评者、作者和评审者——来管理超过400个代码仓库带来的认知负荷。她分享了AI如何提供所需的“内存”,以整合遗留系统上下文、压力测试设计并加速高层架构决策。
个人简介
Julie Qiu 是谷歌云软件开发工具包(SDK)的超级技术负责人,她负责构建跨不同语言生态系统的客户端库和命令行工具,以便与谷歌云产品交互。在此之前,Julie 是 Go 安全团队的技术负责人,她领导了 Go 对漏洞管理的支持以及 Go 的包发现站点 pkg.go.dev 的开发。
关于会议
QCon AI 是一个由实践者主导的会议,完全专注于安全扩展这些工作负载所需的工程学科。它提供了对同行组织在生产中使用的架构手册和故障指标的直接访问。
INFOQ 活动
/filters:no_upscale()/sponsorship/eventsnotice/3ecacfe1-02d1-4d54-a048-8ee8571a77bb/resources/1EonWebinarMay21-transcript-1774373522295.png)2026年5月21日,美国东部时间中午12点
/filters:no_upscale()/sponsorship/eventsnotice/1302f11a-f90f-4a79-96d1-3dd20d032144/resources/1HarnessWebinarMay28-Transcripts-1776246863928.png)2026年5月28日,美国东部时间下午1点
主讲人:Eric Minick - Harness DevOps 解决方案高级总监,以及 Aaron Newcomb - Harness 高级产品营销经理
/filters:no_upscale()/sponsorship/eventsnotice/7dd71c7c-4b0e-4760-b97d-232ac1816637/resources/1NeuBirdWebinarJune25-Transcripts-1777458459989.png)2026年6月25日,美国东部时间下午1点
主讲人:Justin Griffin - NeuBird AI 产品负责人
演讲文稿
Julie Qiu:我叫 Julie。我是谷歌的高级首席工程师。我目前是谷歌云 CLI 和 SDK 的超级技术负责人。在我加入谷歌云之前,我在 Go 编程语言团队工作了几年,并领导了 Go 安全团队。今天,我想和大家谈谈,在我探索大规模工程系统的过程中,如何将AI用作思考伙伴。我的团队构建了开发人员用来与谷歌云交互的工具。那就是 gcloud CLI 以及目前支持九种不同语言的客户端库。这些是谷歌云开发人员用来进行身份验证、调用 API、管理资源和自动化部署的工具。在许多方面,它们定义了您在使用谷歌云时的开发者体验。在座的各位有多少人使用谷歌云?您可能对我们开发的许多工具都很熟悉。如果您没用过,它们看起来是这样的。这是一个用于创建新存储桶的标准 gcloud 命令。这是用 Python 进行的相同操作。然后这是用 Go 的样子。您看到的 Go 和 Python 代码就是我们所说的客户端库。它们是围绕 API 创建的特定语言包装器,这使得在您选择的语言中使用起来很自然。理论上,如果您看一下,这实际上是一个非常简单的过程。我们有一些团队,他们的工作是定义 API。我们称这些为服务团队。比如存储、Pub/Sub、BigQuery。每个服务团队都是实际拥有该服务的团队。他们是定义 API 及其所表达接口的团队,例如它接受的参数或暴露的方法,以及返回的所有数据。这些团队使用共享的规范格式描述他们的 API。然后,这些规范就是我的团队用作生成器原材料的依据。接下来,我的团队要做的是,为所有这九种语言运行生成器。我们采用相同的基础 API 描述,然后我们会添加诸如如何进行身份验证之类的层,或者我们会添加一些表层优化以使其更好用。我们会添加云功能、产品功能、语言功能等等。然后,我们通过将它们发布到各自的包管理器(如 PyPI、npm、Maven 等)来向用户发布这些库。
关键在于,这个系统的底层实际上非常复杂。当你面对一个多语言系统,经过几十年的演进,多个团队共同协作时,各种不一致性就会自然而然地逐渐显现。所有这些差异在最初出现时可能都非常微小,并且在当时的背景下可能也极其合理。随着时间的推移,整个系统变得难以看清。问题不在于任何一个具体部分有多难,而在于它太大了——大到无法放入一份文档,大到无法在白板上画出来,甚至大到无法装入一个人的大脑。
两年前我刚加入团队时,我以为只需要收集证据就够了。我在团队中启动了一个信息收集流程。我告诉大家:你们只需要提交 bug、记录所有遇到的阻碍、提出所有想法,然后我们把所有数据汇集起来,就能弄清楚状况。我想,如果能把所有问题和历史背景集中在一处,解决方案自然会浮现。这个收集流程确实有效,它让我真切地看到了团队日常需要处理的事务有多么庞杂。但我没能看清的是,在所有这些海量信息、边缘案例和长尾问题背后,我无法把握容纳这一切的系统全貌。我被这些细节淹没了。每次尝试退一步观察,总会遇到另一个不同的问题。我能感觉到自己存在盲点,却始终无法让整体图景清晰起来。原因在于我的设计思路未能完全贯通——我知道哪些部分重要,就是无法将它们有机组合。
AI 的五种独特角色
今天我想谈谈 AI 如何帮助我理解这个系统,以及它如何协助我重新设计未来架构。通常我们讨论软件领域的 AI 时,焦点往往是开发效率——如何更快地编码、代码助手、自动补全等功能。但作为管理 70 多人团队的技术负责人,我的瓶颈从来不是打字速度,而是信息承载能力。我不需要 AI 替我写代码,我需要的是更大的“内存”——一种能承载整个组织 400 多个代码库动态状态的方式,让所有相关信息在我需要时自动浮现。我需要摆脱把所有事情都塞进大脑的困境。
过去几年我用 AI 解决这个问题时,逐渐发现了五种独特的应用模式。首先是 AI 作为考古学家:用它梳理系统脉络,拼凑出真实运行状况。其次是 AI 作为实验者:通过模拟我的想法,在投入工程师数月工作量之前验证方案的可行性。第三是 AI 作为批评家:我会直接询问“指出我设计中的问题”,它会坦诚给出分析。第四是 AI 作为合著者:实际用它编写生产级代码。与之对应的是 AI 作为代码审查员:在提交人工评审前,它能发现潜在问题并厘清逻辑,提升代码质量。
假设 - 简化
在开始这个项目之前,我有一个假设。我的假设是,我们可以让这一切变得更简单。我们不必让每个人都经历如此耗费认知的过程。我们可以找到一种更合理、更通用的协作方式。我认为,与其让每个语言团队各自去实现自己的构建、测试和发布流程,不如将其整合为一个统一的生产流水线:一个 CLI 工具、一个发布流程、一个技术栈、一个配置系统。我想要的系统,是那种足够优雅,足以统一我们所有工作流的系统;足够简单,让我的语言团队可以专注于能带来产品价值的特定语言工作;同时也要足够灵活,当我们需要根据产品和语言特性进行差异化处理时,系统能够支持我们。理论上,这似乎是可行的。
然而,在几个月的时间里,与不同团队尝试解决各个部分后,我们不断遇到各种阻碍。每一次尝试都暴露出不同的挑战。第一次尝试,我们决定为一种语言设计一个端到端的完整系统。有些人称之为“钢线法”。当时,这感觉是一个非常合理的方法:选择一个语言,深入研究,让它运作起来。但很快,在几个月内,我们就意识到我们承担了太多。系统也开始朝着我们选择的那个语言的方向塑造。随着时间的推移,我们意识到,尽管核心想法仍有前景,但迁移到这个新系统将需要数年时间。此外,在项目开始时,我并没有完全意识到技术设计与背后工程师的理念是如此紧密相连。当你是一名工程师,并且致力于一个特定的语言系统时,你会非常关心那个语言的生态系统及其背后的细节。有时,从架构角度看,这些细节并不真正重要。给予工程团队灵活性,让他们感到参与其中并被倾听,是使这个项目成功的关键组成部分。我理解这一点,因为作为一名工程师,我可能会因为别人的要求而去做某件事,但如果我感到你理解了我所面临的挑战,我就更有可能积极参与。
我们重新开始,进行了第二次尝试。第二次尝试中,我们缩小了范围。我们没有一次性构建所有东西,而是只取了系统的一个薄片,并选择了两种语言。我们打算同时处理这两种语言。很快,细节再次变得难以应付。由于过早地尝试通用化,我们最终得到了一堆过于通用的抽象,使得在特定语言领域之外,状态完全无法管理。我们还遇到了人员限制。在决定构建这个流水线时,我们选择用 Go 语言来构建整个系统。我们为这个项目组建的团队来自不同的语言生态系统,实际上,教他们 Go 语言并让他们学习一门新语言,又成了另一个瓶颈。我们不得不再次重启项目。
这次之后,我感觉自己已经阅读了数百份设计文档,阅读了大量代码,评审了无数 PR。我觉得需要完全重置,从第一性原理开始。我回过头来,追溯了我们是如何走到这一步的。然后,我打开终端,创建了一个实验目录,里面有一个空文件夹,并创建了一个 Markdown 文件。在这个 README.md 中,我写下了我脑海中系统的最简单版本。其核心是,CLI 有三个职责:第一,它必须读取状态并管理该状态,然后在必要时能够将其写出;第二,它必须接收规范和配置,并据此创建客户端库;第三,它必须发布并将其推送到包管理器。
我拿着这个设计,打开 Gemini CLI,对它说:读取这个文件,并按照我的描述为我构建它。只需一个提示,它就做到了。这太神奇了。它给了我 CLI 框架,给了我标志解析,给了我命令结构。当然,这只是最枯燥的部分。没有迁移策略,这个工具就只是个玩具。如果不先理解生态系统,我就无法将我闪亮的新工具应用到我想要的生态系统中。
AI 作为考古学家
这就引出了 AI 的第一个角色:考古学家。当你在一个历经多年构建的系统中工作时,文档往往是过时或不完整的。你唯一真正能信任的,就是实际运行的代码。如果你接手了 400 多个代码库,并且必须去阅读所有代码,这将是一个非常缓慢、非常手动且非常耗费心力的过程。我决定从小处着手。我挑选了一个代码库。我知道在我们 Python 代码库中,大部分配置都集中在一个地方。我将其克隆到本地。然后,我再次打开 Gemini CLI,问道:Python 生成器到底是如何工作的?它接收什么输入?它产生什么输出?同样,通过一个简单的提示,AI 就将数千行代码提炼出其核心行为。它识别了输入、处理过程和输出。它甚至告诉了我一些我根本不知道存在的文件。它还端到端地描绘了生成工作的全貌。这确实帮了我大忙,因为通常这需要我花费数周时间才能完成。接着我想,如果把你刚刚发现的所有文件整合成一个 YAML 文件会怎样?告诉我那会是什么样子。它又一次做到了。它给了我一个答案。这个答案并不美观。我想这个文件可能超过一万行。但它确实给了我一个起点,并立即引出了更多问题。比如,这些正则表达式在这里做什么?这些元数据中的一部分是否可以从其他字段推导出来?我们真的必须硬编码所有这些字符串吗?这个 API 配置中,哪些部分实际上是 Python 团队特有的?有多少内容在九种不同的语言中被重复实现?它本应存在于一个语言无关的层面。我不知道答案,所以我继续询问 AI。我花了几个小时与它一起工作,逐步弄清楚 Python 生成流程是如何端到端运作的。它给我的答案并不完美。有时我不得不停下来,因为我看着代码库说:你确定你提到的这个文件夹真的存在吗?当然,我总是对的,它会告诉我:是的,它不存在。这才是实际存在的。其他时候,我需要给它更多上下文。我会说:看看这个 GitHub issue 或者这个文件。这会改变你的分析吗?
有时我也会出错。它会遗漏文件。它会遗漏参数。它会编造新的文件,编造新的参数。而且每次它都非常自信。实际上,有些问题直到我开始构建原型并发现某些东西不工作时,我才会注意到。经过几个小时的来回交流,我确实学到了很多。我对 Python 生态系统的了解比开始时深入得多。我还不确定如何处理所有这些信息,所以我只是请它将我们的对话记录到一个 python.md 文件中。然后我对 Rust 也做了同样的事情。我使用 Gemini 深入挖掘生成器的工作原理、它使用的配置、所有的输入和输出。我发现 Rust 有着完全不同的理念。例如,Python 喜欢将其多个主要版本捆绑到一个包中。而 Rust 团队喜欢为每个主要版本发布一个 crate。Python 团队有一个主要的配置文件,超过 4000 行。Rust 团队则每个文件夹有一个配置文件,通常只有大约 10 行 YAML。Python 团队有很多自动化流程。Rust 团队喜欢在笔记本电脑上运行任务。当我为每种语言重复这个练习时,我意识到模式非常清晰。每个人实际上都在试图解决相同的高层问题,但随着时间的推移,实现方式已经出现了巨大的分歧。AI 使我免去了数周的重复性劳动:安装每个生成器、浏览代码库、阅读大量旧文档,而我实际上能够重建很多逻辑。到最后,我有了一个文件夹,里面装满了各种语言的 Markdown 文件,总结了每个流水线是如何实际工作的。当我通读它们时,我能看到现有系统形态的相同之处和不同之处。一些我之前没有的东西浮现了出来,那就是这张地图。我第一次非常清楚地认识到,系统实际上被分成了三个非常具体的部分。有些组件显然应该由服务团队拥有,因为这些组件特定于那些产品团队和他们试图创建的产品。有一个组件应该由平台团队拥有。因为这些是语言无关的基础设施,用哪种语言编写并不重要。然后还有一个组件,其中特定于语言的知识对于创建地道的开发者体验至关重要。语言团队应该只专注于系统的这些方面,因为这需要他们的专业知识。而我们之前所做的,实际上是让语言团队拥有了一切。我们现有的组织结构正是导致这些差异的原因。当我比较不同语言的模式时,我终于能够分辨出什么是真正不同的,什么只是因为历史原因而看起来不同。
AI 作为实验者
当我意识到这一点时,整个设计思路突然变得清晰起来。我认为这个 CLI、这个发布系统和这条流水线,确实比我们之前的尝试看起来更可行。我终于对这个系统有了足够的理解,能够想象出可能存在的形态。这时,AI 的第二个角色显现出来——作为实验者。
一旦我理解了系统并看清了其轮廓,我就可以开始提出完全不同的问题。在此之前,AI 只是为我挖掘过去,帮助我理解现状。但设计并非发生在过去,设计发生在你的想象中,然后你需要就你真正希望它成为的样子做出艰难的权衡。
重新设计像我们这样的系统成本极高。如果我对 Python 有疑问并想做一个原型,我就得让 Python 团队的某个人停下手中的工作一周。如果我想在 JavaScript 中验证同样的想法,我就得去找 JavaScript 团队的经理,申请同样的人力资源。如果我要在九个不同的团队中这样做,并且还要跟踪所有这些工程师的工作,那么仅仅为了回答一个问题——“这个想法是否可行?”——就需要大量的协调工作。你不能要求一个工程师团队去阅读数十万行代码,仅仅是为了寻找某种模式。但你可以要求 AI 去做这件事。
对我来说,AI 从此变成了一个低成本的架构理解模拟引擎。我可以拿着我脑海中的这个心智模型——我认为可行的想法,以及我看到的那些分类、共享概念和差异——开始进行实验,而无需依赖我的团队。到这个时候,我已经花了数天时间研究我们的生成和发布工作流,对可行的方案有了各种假设。我开始将 AI 作为一个原型设计伙伴,这样我就可以设计系统并理解它,而无需承诺构建完整的系统。
我设置了与之匹配的开发工作流。在我的主分支中,我只与 Gemini 讨论设计。我说,不要写任何代码,只专注于设计。我只希望你和我对话。然后,我使用 git worktree 在代码库中设置了三个不同的目录和多个分支:一个用于 Python,一个用于 Go,一个用于 Rust。这是我决定选择的语言,因为我知道它们各自有非常不同的实际约束。由于有了这些独立的分支,我可以在每种语言上自由实验,而不用担心它们相互冲突。
接着,我写下了这段提示词,给 AI 分配了一个角色,告诉它它是我的设计伙伴。这样,每次我打开一个新会话或在不同的标签页中打开时,我都可以让它读取这段提示词,它会告诉它我所有的工作成果存放在哪里,我已经考虑过的一些替代方案,以及我正在处理的待办事项列表。
在每个特定语言的分支上(例如,在 Rust 分支上),我会说:去读一下这个 prompt.md,了解我正在尝试做什么,然后用那种语言实现它。在这里,作为实验者的 AI,其重要性不在于代码是否完美(代码肯定不完美),而在于实现过程恰好暴露了设计中哪些地方定义不清、哪些地方很别扭,或者它为我凭空想象了哪些想法。
一旦我设置好这一切,我就可以真正开始测试我所有的假设了。我的一个假设是:也许我们的配置实际上是相当冗余的。例如,我注意到,当你查看配置文件时,全局文件写下了所有版本,但每个特定语言的文件也写了。对于 Python,它在 version.py 中;对于 Go,它在 version.go 文件中;而 Rust 有这个 Cargo.toml 文件。这似乎是不必要的。于是我和 Gemini 讨论,我说,我们是否应该直接从 YAML 配置文件中移除版本字段?它已经存在于 version.go、version.py 和 Cargo.toml 中了。当然,我绝对是正确的。单一事实来源更好。移除重复。我继续实验并实现了它,然后很快发现,实际上我提到的一些文件是使用全局配置文件生成的。只是并非所有情况都如此,这就是为什么我没有立刻发现。这个实验让我很容易地没有错过这个细节。然后它开始为我提出其他问题,比如:也许所有的版本文件都应该是生成的。为什么有些是手动构建的,而有些是生成的呢?
在命名方面,我还尝试了另一种模式。如果你查看这个文件,会发现库名、输出文件夹以及 API 路径之间存在很强的关联性。我询问了 Gemini:仅通过查看名称,我能在多少情况下推断出输出路径和 API 路径?它的发现非常有趣。它告诉我,名称、API 路径、输出目录以及所有库都在使用的这个 YAML 配置文件,都遵循一个跨所有语言的通用模式。例如,如果名称是 grafeas,那么 API 路径将是 grafeas/v1。它应该位于名为 packages/grafeas 的文件夹中。然后配置文件将是 grafeas_v1.yaml。实际上我能做的是:我们有一个存储库,包含了所有服务团队编写的 API 规范。我可以编写一个脚本来遍历该目录,然后基于该存储库中的数据做出大量推断,而不是手动维护这个列表。这将实际上从每种语言的配置文件中消除大约 100 个条目。
我还进行了一项实验,这个实验基于一个非常强烈的假设。这个想法是:是使用一个大型的全局文件更好,还是应该使用许多小文件?因为从理论上讲,拥有一个超过 4000 行的大型文件似乎不太具有可扩展性。毕竟,Google Cloud 会不断增长,我们将添加更多 API。然后我从实验中意识到,由于许多字段是冗余的,当你把所有冗余内容都移出后,实际上使用这一个单一文件要好得多。这使得模式变得非常明显,重复变得非常可见,进行搜索和替换等操作也变得非常简单。在我进行这个实验之前,这对我来说感觉非常违反直觉。我认为每个 crate 一个文件实际上要好得多,而且 Rust 的设计更清晰。这个实验帮助我改变了一个重要的假设。
我还利用 AI 来帮助我思考 CI 接口本身。我反复思考的一个问题是:我应该使用位置参数还是标志?最初,generate 命令使用了这个 name 标志,但后来我意识到在所有库的生成过程中它都是必需的,所以它并非必要。然后我可以更新我的 README.md,在设计更改之后,我会告诉 Gemini 在我每个特定语言的分支上进行 rebase,然后让 AI 更新所有内容并重新运行所有实验。这让我在处理每种语言的细节时,能够保持核心设计的同步。更重要的是,它实际上帮助我发现了细微的不一致之处,那些因为我每次都绝对正确而听起来很好的地方,以及那些我绝对不正确的地方。
最后,我还使用 AI 来确认我是否真的能够迁移所有遗留配置,这非常棒,因为通过所有这些模式匹配,它实际上可以帮助我确认遗留文件和我试图创建的文件确实能够工作。所有这些实验的另一个有趣之处在于,它帮助我揭示了许多我以前根本无法看到的东西。例如,所有这些语言生成器,它们生成了客户端库,但实际上它们还生成了许多其他文件。有些生成了 README,有些生成了变更日志,而这些 README 和变更日志的内容会略有不同,因为它们位于特定语言的生成器中。一旦我看到了这一点,我就想,我们可能应该对此进行标准化。这似乎是我们应该做的事情,可以改善用户体验,也更容易维护。另一方面,我在输入方面也看到了同样的情况。有些语言团队让每个服务团队维护这个包含语言特定提示的 YAML 文件。有些有这些 Bazel 目标。而有些则只是使用了他们自己设计的粘合代码。可能每个选择在做出该选择的上下文中都很有意义,但当你从整体上看待整个情况时,很明显你实际上可以消除所有这些文件,或者至少将它们压缩成一个单一的真相来源。
AI 帮助我进行所有这些实验的一个意想不到的好处是,它不仅在设计上帮助了我,还让我能够比平时更早地与我的团队进行更有趣的对话。AI 帮助我将想法推进到一个可以实际分享的阶段,然后与我的团队核对,看看他们的想法是什么。这种来自人类的反馈非常有价值。它向我展示了我的思维模型在哪里与现实相符,在哪里有效,在哪里无效。它甚至暴露了 AI 产生幻觉的地方,但它曾让我确信我是绝对正确的。然后当我向我的团队展示时,他们对我提出的一些事情感到困惑是可以理解的。AI 帮助我勾勒出足够的内容来回答一些简单的问题。比如,这个工作流程真的有效吗?我是否遗漏了一些显而易见的东西?这不是我自己能够如此快速想出来的,但让我的团队更早地参与这个过程,确实帮助我们推动了设计的进展。
AI 作为批评者
至此,我已经有了用户指南、设计文档和许多原则。人们可以对我提出的宏大构想做出反应,但他们不会去仔细推敲 Gemini 在凌晨两点做出的每一个设计决策。我需要一种不同的反馈来帮助我处理这些细节。就在这时,AI 不再扮演我的实验室伙伴,而是开始扮演一个截然不同的角色——AI 作为批评者。
我开始要求 AI 专门挑我设计中的毛病。我不再问“这个可以做成什么样”,而是问“我哪里错了?哪里可能出问题?”它帮我做的最有见地的事情之一就是,我会问:“这个设计的哪些部分看起来过度设计了,哪些部分似乎根本没必要?”
例如,我在查看 Go 配置时,注意到几乎每一行都有这个 remove_regex 字段。我当时想,我们能去掉它吗?它总是相同的吗?AI 去查看了 185 个库,发现在其中的 179 个里,它都遵循一个完全可以预测的公式。只有六个库确实需要这个 Go 特有的配置。这让我删除了 3600 行配置,这简直不可思议。
它并不总是对的。另一次,我请它帮我简化发布工作流。我说,我们是不是应该只有一个发布命令?它可以提交、打标签、推送、发布,我们会设置所有这些默认值,然后只用标志来跳过某些步骤。当然,我绝对是对的。这正是 cargo release 所做的。我们为什么不这样做呢?这是一条久经考验的可靠路径。
然而,当我刚把这个想法带给团队里的工程师时,他们立刻说:等等,那么它默认会发布吗?执行标志也会创建标签吗?如果发布标志失败了,那我如何在不重新打标签的情况下重试?还有,你为什么又把发布默认关掉了?这个命令不就是为了发布吗?
AI 在帮我提供这些教科书式的解决方案,它们在纸面上听起来非常简单。但这个简单的接口,当你把它放到一个团队协作发布工作流的上下文中时,实际上让事情变得更加复杂。你现在必须理解默认值是如何工作的,而不是简单地运行一个个独立的命令来逐步完成所有事情。
AI 作为批评者为我所做的是,它持续地迫使我质疑自己的决定,并为我正在做的事情提供理由。对我来说,仅仅有这种审视就是真正的价值所在。
在这个阶段结束时,我拥有的不仅仅是一个原型。我拥有一个从多个角度被审视过的设计。它通过批评得到了简化。它被精炼到了让我觉得可以为之负责的程度。AI 给了我一种方法,在让数十个团队投入数月工程工作到这个项目之前,对我的想法进行压力测试。最终,我感到有信心去真正地在生产环境中实施它。
AI 作为作者
这时,AI 就成了我的合著者。根据我的经验,你不能直接说“把整个东西写出来”。至少,如果你想要的是能真正展示给别人、并且感觉可以长期维护的东西,就不能这样。就像你分配任务给任何人一样,你必须具体说明你要它做什么。你必须把任务分解下去。你必须预料到你需要审查最终结果。当我用 AI 编写 Go 代码时,我持续发现了一些怪癖。它对所有东西都过度注释。我指的不是非常有用的文档,而是它真的会逐行叙述发生了什么。例如,我想写一些代码来读写一个 YAML 包。它做的是告诉我它正在定义输入和输出文件名,它正在读取 YAML 文件,然后它正在解组数据。所有这些注释都没有告诉我任何单看下面那行代码所不知道的东西。有趣的是,这些注释复述了语法,并描述了代码在做什么。讽刺的是,实际上有这样的注释反而让代码更难理解,因为我现在必须过滤掉注释,过滤掉它产生的噪音,才能看清结构。还有一点。它喜欢空白。我指的不是为了可读性的空白,我的意思是它喜欢添加空白,因为出于某种原因,空白让一切都变得更好。在这个例子中,我有一个 switch 语句,你可以看到,对于每一个 switch 语句,它并没有做什么特别的事情,它只是运行该语言的生成器。出于某种原因,当我使用 Gemini 生成这个时,它在所有 case 语句和所有函数之间都加了一个空行,所以它实际上打破了我写 Go 时通常习惯的视觉分组,而这些 case 在概念上是相同的。这些空白真的只是毫无理由地让人觉得它们不相关。还有一个。喜欢检查 nil,因为我们都喜欢安全,我们都讨厌 panic。在这个例子中,这个函数 createLibrary 所做的是,如果你传入的变量是 nil,那么返回 nil。然后它调用一个辅助函数,该函数是空的,然后它又做同样的事情。虽然 AI 试图确保安全,但它实际上使 API 契约变得模糊,因为我应该期望它一开始就愿意接受一个 nil 值吗?AI 试图确保安全,但我认为它实际上并不理解安全属于哪里,所以它没有一次性建立一个清晰的不变量,而是到处检查 nil。还有一点,我觉得非常有趣,它总是忘记运行 Go 格式化工具,比如 gofmt 或 goimports,我得到的代码中导入没有分组在一起,或者间距不对,我不得不提醒它在最后运行所有这些命令。对于任何以前写过 Go 的人来说,你可能很熟悉这些是定义得很好的规范。这不是我想出来的某种风格。有公开的工具。它是标准化的。有趣的是,感觉 AI 生成的东西看起来像 Go,也有点像 Go,但它并没有真正理解 Go 开发者用来相互交流的结构。
不过,有些事它做得非常出色,我也变得非常依赖它来完成。其中之一是按字母顺序排列。我觉得字母排序很有挑战性,但我很喜欢能把一个结构体交给 AI,然后说:帮我把这些字段按字母顺序排列一下,它就能轻松地重新组织好一切。另一件它做得特别好的事情是重构导入语句。当我移动代码时,如果需要将一个包从一个地方移到另一个地方,并且在代码库中到处都有引用,AI 就变得极其有用,因为它会直接帮我重命名所有这些导入。而在过去,我必须弄清楚要运行什么命令才能实现这一点。另一个我非常喜欢的实用功能是用它来修复编译错误和 linter 错误。我在重构代码时经常遇到的一个情况是:我有一个函数,想传入另一个参数,现在必须追踪整个调用栈,找出所有需要传递这个参数的地方。我通常的做法是运行 go test,得到编译错误列表,然后逐个文件去编辑。而现在,我只需告诉 Gemini 去修复它,把所有的错误都交给它,它就能准确无误地完成,这真是太棒了。它会更新调用点,传播参数,让一切重新编译通过。我认为,在使用 AI 的过程中,我不断看到的一个模式是:如果你给它一个具体的、重复性的任务,并且正确性标准非常明确,它就是一个惊人的加速器。我还发现,如果我给它一个风格指南,输出质量会显著提高,更接近我想要的结果。例如,我曾传入 Go 官网的文档,比如《Effective Go》手册、Go 注释规范、Go 格式化指南,或者关于结构化日志输出的博客文章,然后我会说:"写代码时,请确保遵循这里描述的约定。" 它会照做,并且大大改善了我想要的代码结果和质量。所有这些工作,如果是在看电视或听播客时,我通常都会自己动手做,但现在 AI 几秒钟就完成了,我只需要审查结果。我仍然需要自己编写重要的逻辑,但对于那些小型重复性任务,AI 让我在极短的时间内达到了同样的质量。
AI 作为审查者
这引出了 AI 为我扮演的最后一个角色:代码审查者。人们最大的抱怨之一就是 AI 生成的代码质量低下(AI slop)。事实是,你生成了代码,然后把它丢给审查者。我对此非常警惕。我将 gemini-code-assist 插件作为工作流的一部分运行,其好处在于,当我发送 PR 时,它会自动检查所有这些事项。有时它会发现缺少错误检查,例如在这个案例中,它发现一个文件本不应存在,但可能存在且可读,这会导致测试本应失败却静默通过。这里有一个例子,它评论了代码结构,并告诉我应该使用表驱动测试。这里还有一个例子,它发现另一个 AI 没有运行我想要的 Go 格式化工具。另一个例子是,我本应使用常量,而不是在多个地方硬编码数字 3,这让我能够保持同步。有时它还能捕获代码编译通过的关键错误。这里有一些我在开会时心不在焉写的东西,看起来似乎没问题,但 AI 立即标记出了两个 bug,幸运的是,在我的团队成员真正开始审查并感到困惑之前,我就修复了它们。
AI 的局限性
就像我们讨论过的所有其他角色一样,AI 也有其局限性。例如,这里有一个我正在为之前提到的 YAML 包创建的 PR,它编译通过,测试也通过了。但我的一个团队成员立刻跳出来说:你注意到这个改动了吗?为什么把它打包进这个 PR?这看起来完全无关。这是另一个例子。我简化了我们的 YAML 结构,AI 给出了一些有用的机械性反馈,但我的一个团队成员立刻说:看到这个重复的函数让我很难过,你能修复它吗?这很有趣,因为当有人说"难过"时,那是一种直觉在说话。两个版本都运行得很好,但这种感觉源于对"优秀代码应该是什么样"的认知。他还问了我一些非常有用的问題,比如:这个项目未来会怎么发展?因为你有一个叫 language 的字段,但你告诉我我们可能也会把它用于 gcloud,那么 gcloud 算是一种语言吗?这是因为工程师在审查你的 PR 时,不仅仅考虑眼前的 PR,他们还在思考路线图、未来的方向等等。AI 无法帮助我回答那些超出我所提供上下文的问题。最后一个例子是关于权衡取舍的讨论。团队中有人说:这个结构体里有很多 sources,我们为什么不把它们合并成一个 map 呢?这样我们就不用输入那么多东西了。另一个人插话说:实际上,我认为我们现在应该保持原样,因为我们仍处于这个流程的设计阶段,明确列出字段反而有助于我们以后重新思考结构。这种决策,只有了解项目背景、我们仍在尝试做什么,以及我们希望更容易捕获哪些错误,才能做出。
总结
我一直在谈论的这个项目,目前仍在进行中。当我开始这项工作时,我真心以为最困难的部分会是弄清楚整个系统在我提到的所有维度上是如何运作的。所有的语言、所有的组件、所有的工作流程、产品,以及长达十年的演变过程。那确实非常困难。当我将 AI 引入这个循环时,它帮助我看得更清晰。然后我意识到,真正的困难实际上出现在之后,那就是:我该如何处理所有这些信息?我该保留什么?改变什么?又该舍弃什么?AI 帮助我照亮了那些角落。它帮我梳理出哪些地方有问题,哪些是冗余的,哪些只是因为我们一直以来都那样做。但它做不到,至今也仍然无法做到的,是取代那些理解、综合和决策过程中混乱且难以言喻的艰苦工作。作为一个考古学家,AI 可以为你重建事物。但它无法告诉你我们当初为何做出那个决定。为此,我需要找到当时参与决策的工程师。作为一个实验者,AI 可以非常快速地为我生成一堆想法。但在我找到真实的团队之前,它无法真正验证我的想法是否正确。作为一个评论家,AI 可以告诉我差异和不一致之处,但它无法取代我队友的判断力。作为一个作者,AI 可以为我编写代码,但它需要依赖我多年的经验才能真正判断那些代码是否优秀。作为一个审查者,AI 发现了大量的机械性错误。而真正发现代码之外的问题——那些需要结合上下文、路线图以及我们试图推动项目前进的方向才能理解的问题——的,是人类。
将 AI 作为思考伙伴的经验教训
以下是我将 AI 作为思考伙伴这段时间所学到的东西。起初我以为问题在于,我只是需要更多个“我”。我只需要能够克隆自己,这样我就有更多的时间、更多的人手和更多的精力。但我意识到,克隆自己并不能解决问题。因为瓶颈从来都不在于我的双手或我的打字速度。它始终在于我的判断力、我的理解力、综合能力以及决定什么真正重要所需要的那种混乱的工作。AI 无法给你这些。它无法取代你的判断力或品味。它无法取代你在某个领域花费数年时间所获得的直觉。这是有原因的。我们讨论过 AI 是如何基于已存在的事物、基于过去的平均水平进行训练的。我认为,最有趣的工程问题实际上并不存在于那里。AI 能做的,是帮助你思考。它可以挑战你的决定。它可以向你提出尖锐的问题。它可以像一位充满好奇心的队友一样反驳你。它可以承担那些你确实希望克隆自己去完成的工作。我认为它之所以能够做到这些,首要原因是你拥有专业知识,知道该做什么,也知道你试图达到的目标。AI 给予我的,是我一直想要的东西。它给了我额外的一双手,去完成那些过于机械而乏味,但又因上下文过于复杂而难以委派的工作。这些工作我知道怎么做,只是我不想把有限的时间和精力花在上面。AI 并没有让我快十倍。它真正做到的,是让我在努力进行的工作中投入了十倍的心力。它让我在那些变得机械的部分获得了杠杆效应。它让我能够将那些部分重新投入到仍在成长的事物中。这就是我看到的 AI 的真正价值,至少对我来说,是让你能够解放自己,将时间花在真正让你兴奋、并且只有你能做的事情上。
查看更多[带文稿的演讲](https://www.infoq.com/transcripts/presentations/)
录制于:

2026年5月15日