将猛兽关在笼子里:Agent 编程之道
2026/5/16 17:41:04 网站建设 项目流程

最近在实践 Agent 辅助编程时,我越来越意识到一个矛盾:Agent 生成代码的速度太快了,快到我还没来得及思考,几千行代码就已经出现在屏幕上。功能确实实现了,测试也通过了,但当我回头审视整个系统时,一种深深的无力感涌上心头——每一段代码单独看都很整洁,每个函数命名规范、逻辑清晰,可它们组合在一起,却成了一座无法理解的“屎山”。

我给它起了个名字:整洁的屎山

每行代码都像出自教科书,但整个系统的信息量已经超过了人脑的承载极限。

Agent 就像一头猛兽。它有无限的精力和速度,你给它一个目标,它会毫不犹豫地冲过去,踩碎一切挡在路上的杂草。但问题在于,它不会主动思考“这条路走完之后,这片土地会变成什么模样”。功能优先,是它天生的算法倾向。

于是我想到了“笼子”。不是要扼杀猛兽的力量,而是让它在我规划好的路径和区域内奔跑。架构、设计、规划——这些在传统编程中被称为“软技能”的东西,在 Agent 时代变成了最硬的约束。

为什么 Agent 特别需要“笼子”?

传统编程中,代码的复杂度增长与人的精力投入是同步的。你写一行,就理解一行。但 Agent 可以在几秒内生成一个完整模块,你的理解速度远远跟不上它的生成速度。如果没有预先设计的边界,你很快就会面对一个“黑箱”——你知道它干什么,但不知道为什么它在某处出了一个奇怪的 bug。

更可怕的是,由于 Agent 倾向于无状态、无副作用的局部解(因为 prompt 里通常只描述了当前需求),它天然会规避全局性设计。结果就是:每个功能独立看都是好的,但功能之间开始互相踩踏,数据流隐式传递,状态散落在各个角落。出 bug 时,你无法判断是 A 模块的锅还是 B 模块的锅,因为根本没有清晰的模块边界。

笼子的三个要素

一个好的笼子,不是铁板一块,而是有筋有骨的框架。基于最近几个项目的摸索,我总结了三个必须提前确定的约束。

1. 接口即契约:先定大门,再砌房间

在让 Agent 写任何实现代码之前,先定义好模块间的接口。用 Python 的Protocol,或用 Java 的interface,哪怕只是写在文档里的一句话也行。

例子:

模块 A 提供: - 输入:用户 ID(字符串),时间范围(起止时间戳) - 输出:订单列表,每个订单包含金额、状态 - 保证:不产生任何数据库写入,不调用外部 API 模块 B 提供: - 输入:订单列表 - 输出:统计报表 - 允许:读取配置缓存,但不允许修改

把这些规则写进 Agent 的系统提示里,然后说:“只实现接口约定的内容,不得引入其他依赖,不得修改接口签名。” 这样一来,即使模块内部写得再乱,它也被关在自己的房间里,不会污染邻居。

2. 状态显式化:用状态机替代隐式 flag

Agent 特别喜欢写if self.state == 'pending' and self.retry_count < 3这样的代码。三五处还好,一旦状态多了,就成了隐式状态机——bug 往往出现在你遗漏的那条状态转换路径上。

笼子做法:先画一张状态转换表。

当前状态事件下一状态动作
pendingpayment_okpaid发货
pendingtimeoutfailed发送告警
paidship_okshipped更新物流
paidrefund_reqrefunding请求退款网关

把这张表作为 prompt 的一部分,要求 Agent 生成的代码只能按照这张表驱动状态变化。你会发现,生成的代码变成了一个“查表执行器”——简单、可预测、易调试。问题出现时,你只需看当前状态和发生的 event,就知道是哪个转换出了问题。

3. 可观测的边界:让每次跨越都发出声音

Agent 写的代码通常没有日志,或者只在最后打印一行“done”。当系统出错时,你面对的是一个大黑箱,不知道请求在哪里卡住了,也不知道哪个模块抛了异常。

笼子做法:要求 Agent 在每个模块的入口和出口,自动打印结构化的边界日志:

[ENTRY] module=payment_gateway, args=..., timestamp=... [EXIT] module=payment_gateway, duration=12ms, error=none

如果发生异常,则打印:

[EXIT] module=payment_gateway, duration=3ms, error=TimeoutError

加上这个约束,一旦系统出问题,你只需要grep "EXIT.*error"就能迅速定位是哪个模块、哪次调用出的问题。问题被自动局限在“一小块”里,修复成本大大降低。

过度设计也是另一种笼子(但打不开)

当然,提醒一句:不要为了“关猛兽”而造一个密不透风的铁笼。如果你对每一个函数的实现细节都做了约束,那 Agent 就退化成了一台打字机,失去了生成能力的意义。

我的经验是:只对跨模块边界和共享状态做强约束,模块内部允许 Agent 自由奔跑。如果一个模块内部也开始变得复杂,就把它拆成子模块,重复上述流程。这是递归的笼子,也是递归的优雅。

实践:一个三层的笼子模板

我目前在项目中使用了一个简单的architecture.md模板,每次启动新模块前先填充它:

# 模块:订单处理 ## 职责 - 接收订单事件,维护订单状态 ## 禁止 - 不允许直接发送 HTTP 请求(应该通过事件总线) - 不允许操作全局缓存 ## 接口 - inbound: 实现 on_order_event(event) - outbound: 发出 OrderStateChanged 事件 ## 状态机 (表格,如上所示) ## 边界日志 - 入口打印 ENTRY - 出口打印 EXIT(含耗时、错误) ## 测试要求 - 每个状态转换必须有对应的单元测试(mock 掉外部依赖)

然后把这份文档直接喂给 Agent,要求它“严格按照上述架构生成代码,不得违反任何一条约束”。结果生成的代码,质量稳定得令人惊讶——Agent 仍然很快,但它的快被引导到了正确的地方。

结语

Agent 是一头猛兽,但它不是敌人。它的速度和能量正是我们需要的。我们不需要驯服它,只需要为它修一个足够大、足够通透的笼子——这个笼子就是架构设计。

有了笼子,猛兽依然是猛兽,但它的破坏力变成了建设力。你可以安心地站在笼子外面,观察它在哪一块地上跑得最快,在哪一块地上摔了跤。而摔跤的地方,不再是一个需要重构整个系统的无底洞,而只是一个可以被轻松替换的小隔间。

速度不可怕,失控才可怕。用结构驾驭速度,就是 Agent 时代的新工程之道。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询