构建 Agent 和写软件,是两种工程

写了十年代码的工程师,上手 AI Agent 时常常比新人还别扭。不是能力问题,是直觉问题——越熟练的旧直觉,越容易在 agent 面前变成包袱。

Google DeepMind 的 Philipp Schmid 在 AI Engineer 大会上花十分钟讲了这件事。他负责 Gemini 的 agent 相关工作,每天在 Google 内外看到工程师卡在同样几个地方。他挑了五个最典型的差异,都是”过去这么写没问题,现在这么想就错了”的那种。原视频:Why (Senior) Engineers Struggle to Build AI Agents

从交通管制员,到调度员

Schmid 先把两种开发摆在一起看。

写传统软件,流程是:写规格(spec 或 PRD),写代码,写测试保证代码按预期跑,部署,交给用户。构建 agent 不一样:你定义一段指令告诉 agent 你想要什么,运行它,观察它做了什么,然后回头调提示词、调工具,再跑一遍。这是一个不断迭代、让 agent 越来越可靠的循环。

他用一个类比说清楚区别。写传统软件,你是交通管制员:红绿灯、限速、哪条路能走,车怎么开全在你手里。构建 agent,你更像调度员:你告诉它”我要从德国去伦敦”,至于坐火车、坐飞机、开车还是从水底过去,由它自己定。你定义的是目标,不是到达目标的每一步。

用过编程 agent 的人都见过,它有时做一些很奇怪的动作,但最后把事情办成了。这正是这套模式想要的:你交出过程,换回结果。

接下来是他挑的五个差异。

一、文本成了新的状态

传统软件里,万物都能映射成数据结构:布尔值、标志位,程序靠检查这些字段决定下一步。

LLM 带来的变化是,它能理解语义。Schmid 举了 deep research agent 的例子(这类 agent 先出一份研究计划,再分步执行)。你提一个市场调研请求,agent 先返回一个研究计划。过去你只能”批准”或”拒绝”这个计划。现在你可以在批准的同时补一句”重点看美国市场,忽略加州”。这种带语义的补充,过去要拆成好几步:拒绝、追加输入、重新生成计划,现在一句话就够了。

记忆和个性化更明显。他举自己的例子:他是欧洲人,平时用摄氏度,但做饭时想用华氏度。过去你只能在用户画像里存一个标志位”此人用摄氏度”,没法根据上下文动态切换。现在这些都不再是结构化字段,而是文本和上下文。当然也可以是图像、视频、音频,但共同点是,我们不再活在那个边界清晰的数据结构世界里。

二、开始交出控制权

Schmid 用客服场景讲这一点。

过去用户说”我要取消订阅”,系统先用一个分类模型判断意图——”这人想流失”——然后进入一段预设好的流程:要不要挽留?怎么取消?整条路都是写死的,没有动态反应的余地。

换成 agent,它可以理解这句话背后的意思,主动提供别的选项,用户可能就改主意了,意图完全变了。而这种多样和独特,用过去那种有状态的固定流程几乎没法穷举建模。所以你得信任 LLM,把控制权交出去,承认自己不再处在一个纯确定性的环境里。

三、错误只是另一种输入

agent 流程里某一步失败了,要把它当成正常输入来处理,跟用户输入没有本质区别。

Schmid 拿 Go 语言打比方:一次函数调用,返回的可能是错误,也可能是值,代码平等地对待两者。agent 也该这样。过去 HTTP 请求很便宜,一次商品搜索失败了,重跑一遍就行,重做所有工作也无所谓。但现在一个 agent 可能要跑五分钟、十五分钟,中间断了,如果从头再来,之前那些步骤的算力全部白烧,而且可能丢掉已经攒下的上下文。

所以不能简单地推倒重来。要把错误反馈给模型,加一些兜底和检查,让流程尽量往前走,而不是退回起点。

四、eval 和单元测试是两回事

写软件时我们写集成测试、单元测试、冒烟测试,背后是一个假设:输入 A 给代码 B,永远得到输出 C。

agent 没有这个保证。它是非确定性的,同样的输入不保证走同样的步骤、得到同样的结果。所以测试方式要从单元测试转向 eval——一套衡量 agent 表现的评估集。测的重点从”对不对”变成”十次里成几次”。一个客服 agent,同样的提示词十次只成一次,那就没法上生产,太不稳定了。

而且 agent 的结果很主观。让它写研究报告,和让它处理客户反馈,”好”的标准完全不同。所以需要更偏定性的评判,比如让另一个 LLM 当裁判(LLM as a judge),或者请人类专家看。你要追踪 agent 的全过程,但最终要基于产出来评估。它可能对某个用户多做四步研究、多花一些 token,但只要最后的结果是你要的,那就是成功。

五、Agent 在进化,API 没有

做过后端、写过 API 的人会有体会:很多接口对你来说是自解释的。一个商品服务里的 deleteItem(id),你一看就懂,不需要写文档说明 id 是什么、失败了会怎样。

但 agent 看不到你的代码,也没有你这些年积累的上下文。它只能看到函数签名、文档字符串和工具定义。所以那个 deleteItem 方法,在它眼里第一眼是看不出在干嘛的。你得为 agent 重新设计 API 和工具:自带文档、语义清晰的接口,而不是默认对方有多年开发经验、参与过这套 API 的演进。

Schmid 的收尾

他把整场浓缩成几条建议:

  • 给予信任,但要验证。 别跟模型死磕,不要硬把它塞进”第一步做这个、第二步做那个”的固定流程。
  • 保住语义。 现在一切都是上下文,没有那么多定义良好的数据结构了。
  • 为恢复而设计。 模型不完美,agent 也不完美,跑得越久越会冒出怪事,得预留恢复的余地。
  • 要评估,不要只断言。 agent 不是百分百可靠,你得找到那个平衡:跑成功多少次,才值得交付给用户。
  • 为删除而构建。 这是每个人都在学的苦涩教训(bitter lesson):软件是可丢弃的。我们会用更好的模型、更好的 agent,把同样的东西重写很多遍。

东西一直在变,所以别太舍不得自己写下的代码。

我的补充:这五点其实是同一件事

Schmid 这十分钟讲得很克制,没有上升到方法论。但把五个差异连起来看,底下是同一件事:工程师那套关于确定性的思维模型失效了。数据结构、控制流、单元测试、自解释的接口,全都建立在”同样的输入得到同样的输出”这个假设上。agent 把这个假设拿掉了。

我自己这一年在做 Agent 落地,这五条几乎条条踩过。挑几个有体感的说。

eval 和写测试,根本是两种活儿。 我给团队的 skill 立过一道准入门槛,核心就是不让”跑通一次”冒充”能用”。一个 skill 在 demo 里成功,和它在十次里稳定成功,完全是两件事。LangChain 去年有个调查很说明问题:八成多的团队上了可观测工具,能看到 agent 做了什么,但只有一半的团队真正跑离线评估。能看见,不等于知道对不对。Schmid 说的”要评估,不要只断言”就是这个缺口。

“错误只是输入”对长任务是刚需。 短请求失败重跑无所谓,但我们有些 agent 任务要跑很久,中间一断从头来,算力和上下文双输。把错误喂回模型让它继续,而不是抛异常退出,这个设计上的转变很关键。

“为 agent 写工具”会改变你写代码的习惯。 过去写接口是给同事看的,文档能省就省。现在 agent 是你接口的头号用户,它只认 schema 和文档。我写 skill 的工具定义时,越来越像在写一份给陌生人看的说明书——因为对 agent 来说,它确实是陌生人。

“为删除而构建”最反直觉,也最值钱。 工程师本能想沉淀、想复用、想建抽象。但在模型半年一跳的节奏下,你今天精心搭的很多脚手架,下一代模型一发布就成了负担。值得长期留下的,其实只有验证那一层:能不能判断 agent 做对了。这一层不会随模型变强而失效,反而越来越重要。其他的,做好随时删掉的准备。

如果只让我留一句,是那个调度员的比喻。从管制员到调度员,难的不是技术,是松手——承认你定义目标,但不再亲手安排每一步。对习惯了掌控的工程师来说,这一步最难。

Schmid 说这些内容在他博客上也有,带代码示例。十分钟的演讲,信息密度不低,值得自己看一遍。

抢沙发

评论前必须登录!

立即登录   注册