---
# System prepended metadata

title: Agent 不是代码生成器：工程化在 AI 时代的重生（一）

---



# 从“写得快”到“验得起”——AI 时代的工程化重构（一）

## TL;DR

- Agent 时代写代码不再稀缺，真正稀缺的是人的注意力，热点和瓶颈从 coding 发生转移。
- 一种有效的实践：**交替迭代**，测试和实现永远只改一边。用**确定性测试 + 快照测试**铸造迭代的**不动点**，把“上一个版本是好用的”固化成可回归的事实。
- 注意力预算管理：将测试分为 核心测试 (CT) 与 回归测试 (RT)。前者消耗稀缺的人类注意力确保“正确性”；后者消耗廉价的 token 确保“一致性”。
- 工程化新义：工程化不再是节制膨胀的“刹车”，而是 Agent 飞轮高速旋转时的“方向盘”。管理 Agent 的本质是管理“反馈闭环”与“上下文预算”。

## “糙快猛” vs 工程化 trade-off 的消失

在软件开发中，我们始终面临两种范式的博弈。
一种可以称之为“糙快猛”，它推崇**快速行动，用最短的时间把想法变成可运行的原型**，甚至直接上线创造价值，在真实反馈中确定方向。
另一种则是“工程化”，它更看重**长期主义**，认为**代码的演进有路径依赖，初期的轻率决策会成为系统性的“技术债务”，导致后续代码在错误的基座上扭曲生长**，强调**规范、架构和组件化**。通过前置的抽象、解耦和标准化设计，严控系统熵增，防止项目膨胀至不可维护。这样确保项目完全受控，但代价通常是牺牲了迭代速度，而这往往意味着错失良机。

过去，开发者和团队常常需要在这两种倾向中做出权衡。Agent 的出现，给两种范式都引入了新的变化。一方面，过去的很多工程化方法聚焦于提高代码复用和节制代码膨胀，这难以追得上飞速发展的生产力。另一方面，当试图直接用 Agent 来“糙快猛”地开发新项目的时候，又因为没有亲手定义每一个函数，很快让代码库彻底成为一团不可知的混沌，完全丢失所有的掌控。

举例来说，目前很多人用 Agent 的手法就是照搬一个“交作业式”的 Agent workflow：

- 整理好需求/spec，例如“开发一个数据获取框架，让用户可以从 /path/to/rawdata 读取 MarketData，要求实现功能 1 2 3 4 5”
- 把 spec 发给 Agent 做实现
- 审核 Agent 实现的代码
- 然后做对比实验/回放验证，争取“敢上线”
- 一旦发现问题，再回到上一轮循环

刚开始使用的时候，我们会体验很好，惊为天人。AI 在很短时间里生成了大量的代码，并且看起来还挺符合预期。但很快瓶颈会集中爆在三件事上：

- Code Review 困难：审查不是自己亲手编写的上千行代码非常消耗心力
- 缺乏信心：即使 review 完，依然缺乏足够的信心去 fearless 地进行上线，可能还是要花费大量时间去做手动的对比实验来验
- 痛苦的返工：一旦在对比中发现了问题，又要**重新迭代**那个痛苦的开发-Code Review-测试流程

最后一复盘，往往会发现整体浪费的时间可能会比自己实现更久，最后一摔键盘，“垃圾 AI，浪费生命”。

**要想迭代快，关键不再是更快地产出代码，而是更便宜、更稳定地得到验证反馈。**

**软件开发中，瓶颈永远在人的注意力**，当工程师从具体的编码工作解放出来，借助 Agent 低成本近乎无限地生成代码时，开发的瓶颈自然地从 coding 向其他工作转移。那么由过去聚焦于编码速度的 “糙快猛” vs 工程化的 trade-off 自然逐渐消失。问题变成，我们如何用工程化思维优化那些 Coding 之外，需要我们参与的部分，让他们不要阻挡 Agent 开发的飞轮？

过去的工程化是节制代码膨胀的刹车，而 Agent 时代需要的工程化，是作为高速迭代中的方向盘。**一个完善的测试框架、清晰的架构和明确的规范，不再是拖慢编码的流程，而是我们用来验证和约束 Agent 产出、实现真正意义上“快速迭代”的必要工具。**

本文就让我们从代码正确性开始。当你面对 Agent 在半小时生成的几千行代码时，你面临的不是“写不写得出来”的问题，而是“敢不敢用”的问题。如何优化开发飞轮中，最基本而又必须人来参与的部分：**我们如何用低验证成本确保 Agent 产出的成千上万行代码是正确的、可靠的、符合预期的？**


## 交替迭代：不再 review 全量代码，只看变化“残差”

交替迭代的底层逻辑非常简单：既然人的注意力是稀缺的，那我们就不去看代码本身，而去看代码变化的“残差”。实践上表现为，**测试和实现永远只改一边。**

它把“写代码”拆成两种模式，并让 Agent 在两种模式之间交替切换：

- **模式 A：只动测试，不动实现。**  
  基于当前现有的工程，让 Agent 去完善测试框架、补充测试用例。此时实现代码尽量不动（最多只在接口层做少量必要调整，保证好 review）。  
  如果新增测试跑不过，默认不是“实现错了”，而是**测试没准确刻画现有行为**——让 Agent 自己把测试修到能稳定反映现状。

- **模式 B：只动实现，不动测试。**  
  基于现有测试框架做工程需求/性能迭代，目标是在完全不修改测试框架（最多只修改少量必要用例）的情况下实现功能并通过测试。  
  如果跑不过，默认就是**新实现有问题**——让 Agent 自己修到满足所有测试。

在这个过程中，开发者本人参与的部分依然很少——测试框架是 Agent 写的，测试用例是 Agent 添加的，功能也是 Agent 迭代的。开发者依然只 review 了部分关键测试样例和少量关键实现代码。这并不像传统 TDD（Test-Driven Development） 一样，开发者需要花费大量精力去做繁琐的测试，伤害 mental health。但从结果上来看，review 需要花费的心力大大减少，对代码的信心大幅增加。

那么这个方式的增益来自哪里？这套方法的巧妙之处在于，它为工程迭代引入了一个非常好的**不动点**。在我们的工程迭代过程中，我们可以永远沿用 **“上一个版本是好用的”** 这个关键信息作为“恒等映射”保证工程项目的可靠性和传递性，而开发者需要审核的仅仅是新功能的迭代和优化，作为 **“残差”** 的那部分。

将该原则具象化为以下两条约束:

- **写测试的时候，应该保持代码不变**（或者只在接口层变动，非常好 review）。如果 Agent 写的测试跑不过，那说明是测试写错了，它就得自己改，直到测试能准确反映现有代码的行为。
- **写功能的时候，应该保持测试框架不变**，最多根据功能变更修改少量用例。如果 Agent 写的功能跑不过测试，那说明是新代码有问题，它就得自己改，直到新功能满足所有测试的要求。

这样，**Agent 就不再是一个只会“交作业”的代码生成器，而是一个可以在每个阶段自我修正的“开发者”了。**

接下来问题就变成：这个“不动点”怎么铸造？怎么让它覆盖足够多的情况、又足够稳定，同时还保持能高效迭代？

## 确定性与快照：迭代的“不动点”

上一章我们引入了一个关键概念：不动点。在交替迭代的 workflow 中，我们永远假设一件事成立——上一个版本是好用的，并以此作为下一次迭代的基准。

要让这个“不动点”真的可用，它至少需要满足两个条件：

- 它必须覆盖到足够多的情况；
- 它必须在版本之间足够稳定。

乍一看，这似乎又回到了传统工程里那些熟悉的词汇：分支覆盖率、功能覆盖率、测试完备性……
问题在于：当测试本身的规模也被 Agent 放大之后，Code Review 成了新的瓶颈。大量测试即使交给 Agent 编写，人工逐条 review 依然极其消耗心力，严重损伤 mental health。这就引出了一个看似危险、但对“不动点”至关重要的结论：

- **不动点并不要求“正确”，它只要求“行为在版本迭代上的连续性”。**

如果测试记录的行为本身就是正确的，那当然更好；但即便它并不完美，只要老版本是可用的，新版本至少不应该出现你没有意识到的 regression —— 这就已经把工程迭代的风险压缩进了一个可控范围。

这个观点在传统的软件工程叙事中并不“政治正确”。我们习惯强调交付结果的可靠性，而“允许存在未完全验证正确性的测试”，听起来像是在“让用户或者实盘帮你测”。但这里的关键并不是放弃正确性，而是**用廉价的 token “白嫖“ 功能迭代的行为连续性**。

在解释这套资源模型之前，我们先要引入两个好用的工具：确定性测试与快照测试

### 确定性测试（Deterministic Testing）

这个词听起来很学院派，但它对这种“交替迭代” workflow 非常重要。我们**必须消除测试中的“噪音”**：如果输出本来就不稳定，你的 snapshot diff 只会把人逼疯。

在单线程程序中，这相对简单，主要关注几点：

- 固定 random seed
- 注意 hashmap 这类无序数据结构的遍历顺序（往往内置了一个 random seed）
- 将获取系统时间戳之类的接口“hook”在最外层，作为输入参数传入

但在多线程程序中，这事儿会变得非常复杂，这里不过多展开，有兴趣的可以看一下 [Foundation DB 的文章](https://apple.github.io/foundationdb/testing.html)，或者 madsim、miri 这样的库。

### 快照测试（Snapshot Testing）

在保证测试程序有了确定性的输出之后，Snapshot Testing 的作用就是快速地把测试的输出结果 dump 下来并保存到 Git 与测试的结果对齐。快照的输出可以是非常大量的、难以肉眼验证正确性的，比如一整天某个标的每秒钟的 mid price，每分钟的 VWAP，在指定信号流下某个策略输出的下单流等。

在 Python 中我们可以非常简单的用 [syrupy](https://chatgpt.com/s/t_6969b3219c748191b2f7513d1cf1afde) 无脑引入一堆 Snapshot Testing（已经投入实践）。核心目标是，只要代码上**我们关心的行为发生了任何变化**，包括数值异常、顺序变化、类型调整，都一定要影响到输出的结果。

```python
def test_mid_price(snapshot):
    df = db3.read("2025-12-01", "bnuswapall_1s_mid_price")
    assert df == snapshot
    # 或者嫌弃输出实在太庞大的话
    assert df.describe() == snapshot
    assert df.shape == snapshot
    assert df.dtype == snapshot
    assert (df.index.min(), df.index.max()) == snapshot
```

### 用快照构建回归测试（Regression Testing）

现在可以回答这章开头的问题了，如何用有限的注意力管理海量的测试？答案引入就是将 Snapshot Testing 作为**回归测试**（下文简称 RT）的实现方式。

回归测试由 Agent 大量生成，**开发者可以不 Code Review 测试的行为**：

- 目标不是证明"输出一定正确"，而是把当前版本的行为固化成基线；
- 后续任何改动只要让行为发生变化，**就一定会在 diff 上暴露**；
- 人只在 diff 出现时介入，判断"这次变化是否符合预期/是否可接受"；
- 回归测试消耗的**仅仅是 token**：让 Agent 多跑、多 dump、多存档；**开发者的注意力投入接近 0**。

具体实践模板包括：
- 大输出回放的统计快照：对一天内某标的每秒 mid price / 每分钟 vwap 等，snapshot df.shape、dtypes、describe()、分位数等摘要。不需要证明这些摘要"绝对正确"，但只要发生"悄悄变坏"的 drift，diff 一定会抓住它。
- Schema / 接口契约快照：对外 API 返回的字段集合、类型、空值约束、错误码等做 snapshot。这类变化通常比数值变化更致命，Code Review 非常适合兜底。
- 边界输入的行为连续性：让 Agent 枚举大量 edge cases（空数据、极端区间、乱序、重复 key、缺失字段、非法编码……），记录"输出摘要/错误类型/日志摘要"的 snapshot。不追求"正确"，只确保行为变化一定会被 diff 提醒，你再决定要不要把这个 case 升级成核心测试去严格定义正确性。

RT 的根本的目的是，让 Agent 从“**上一个版本还不错**“这个事实中获取信息。通过快照测试，我们将这个信息固化下来，Agent 的每次修改，都必须以不破坏这个“不动点”为前提。**如果快照有变动，那么这个变动就是我们上一章所说的“残差”**，是我们需要重点审查的部分。

## 并行核心测试（Core Testing）与回归测试（Regression Testing）

通过上文的方法，我们用近乎零注意力成本的方式构建了大量回归测试，但我们依然要回答本章开头提到的另一个问题：**测试都不关注正确性了，那我们的交付质量谁来保证？**

这个问题的回答非常简单，那就是继续**保留“需要关注正确性的测试“**，我们这里称之为**核心测试（CT）**。CT，简而言之，就是原来该怎么测试现在还怎么测试：

- 人工挑选最值得测试的关键路径/关键行为
- 输出尽量"小而清晰"
- 编写测试可以交给 Agent，但**开发者一定要确认 expected output 的正确性**
- **CT 消耗的关键资源就是注意力：你要看它"对不对"。**

既然 RT 的构建过程完全不消耗注意力，那我们可以保证**引入 RT 完全不会影响我们在 CT 上的资源投入**。那么我们就回答了这个问题：**用 CT 保证了不低于传统开发 workflow 的正确性，而用 RT “白嫖“到了功能迭代的行为连续性。**

另外，一个实用的实践是：一旦 RT 中的某个 diff 反复出现，说明它测试的行为是经常改动的关键路径，很值得被就"提纯"为 core test（补上语义断言 + 人工认可的 expected output）。

**注意力永远花在刀刃上**

### 工程上怎么区分这两类测试？

实践上不需要很复杂，最朴素的约定就够了，关键是**让“review 与否的约定”可执行**：

- **目录分层**：  
  - `tests/core/`：core behavior tests  
  - `tests/regression/`：regression tests（快照为主）
- 迭代时，两类都跑，但对于变更的 review 规则不同：  
  - core：PR 必须人工确认断言语义/expected output；  
  - RT：PR 不要求逐条阅读 baseline，只在 snapshot diff 出现时人工判断“变更是否合理”。

有一个小的误区需要澄清一下：Snapshot Testing 只是一种工具，不是专为 RT 构建的。
- CT 中一样可以使用，只要你**认真看了生成的 snapshot 是否是正确的**。
- RT 中也不一定要使用，比如你关心的运行覆盖某个分支的代码之后只要不 panic 即可。

## 工程化思维，而非工程化实践

我们在开头强调了工程化是 Agent 时代快速迭代的必要前提，我们在上文大量引用了工程化的很多经验。但需要再次强调的是，我们要建立的是 Agent 时代的**工程化思维**，而不是强行复用传统的**工程化实践**。相比于传统的工程化实践，我们的 workflow 会有大量“离经叛道”的操作（例如忽略测试的正确性）。工程化思维的本质，是对于一个长期维护的项目，在考虑了各种维护操作（如功能实现、性能优化、文档沉淀、组件重构、测试完善等）会使用到的各种共享资源（如开发带宽、团队 mental health、机器成本、维护负担、Review 成本等）之后，为了完成一系列目标（交付速度、交付质量、扩展可能性等）得到的一些相对较优的实践。

传统工程化实践的问题在于，它背后的成本模型在 Agent 的影响下已经有了很大的变化：

- **coding 成本**：变得几乎可以忽略不计，且带宽大得可怕。以前，代码是稀缺资源，即使是算法高手，产生大量代码也需要消耗很多心力。现在，Agent 可以在几分钟内产出几千行代码。
- **测试/文档的编写和维护**：变得不再枯燥乏味，不会对开发者造成很大的心智负担。以前，很多工程师讨厌写测试和文档，因为这不仅枯燥，还需要在功能改动时保持最新。现在，我们可以让 Agent 去做这些事情，人类只需要验证它们的质量。
- **Context**：成了真正的稀缺资源。模型的 context 窗口是有限的，如何在有限的 context 中放入最关键的信息，成了新的挑战。
- **协调者的思考时间**（包括 Code Review）：成了稀缺资源。一个人能同时协调多少个 Agent？一个人能在多少时间内做出高质量的决策？这成了新的瓶颈。
- **Prompt**：成了稀缺资源。一个好的 prompt 或者 prompt workflow 需要精心设计，需要对问题有深入的理解。这需要人的思考和创意。

我们可以画一张图来理解这个新的资源模型。

![1](./1.png)

我们可以感受到，跟开发者相关的交互都是绝对的瓶颈：

- 精心设计 Prompt
- 阅读 Agent 给出的变更解释
- Code Review 项目的变更代码
- 为项目或 feature 做架构设计

为此，**我们的“工程化实践”必须是基于这种新的资源模型下重新建立的**。

最后，我想强调一点：我们倡导工程化作为手段，但像以前的“糙快猛”一样能实现**快速迭代**才是我们的目的。我们不是要像传统大厂那样，一个项目设计几个月，开发几个月。我们倡导的工程化只是手段。因为在 Agent 时代，**工程化反而成了快速迭代的最佳方式**。

## 结语：工程化在 AI 时代到底“管什么”

写到这里，其实整篇文章只想把一件事说清楚：Agent 让 **Coding 带宽** 变得几乎不稀缺，于是开发的主战场从“写得快”转向了“验得起”。工程化在这个时代的意义，也就跟着变了。

很多人会把瓶颈归因到 Code Review：Agent 像风一样产出几千行代码，人当然看不过来。但这更像误诊——在很多严肃场景里（比如策略迭代），真正最费时间的往往不是 review，而是为了“敢上线”去做的对比实验、回放验证、以及线上问题的定位成本。**验证才是最贵的那一段。**

所以我更愿意把“用 Agent”当成一个管理问题：你不是在管理它写了多少代码，而是在管理三个东西：

- **反馈闭环**：交替迭代 + 不动点，让 Agent 在边界内自我修正；让“上一个版本是好用的”变成可以自动回归的事实。
- **稀缺资源的预算**：prompt、context、人的注意力、以及验证周期。token 不是瓶颈，人脑才是。
- **长期记忆**：把一次协作沉淀成仓库里的文档、`AGENT.md`、Skill，以及那些看起来很不工程化的 ad-hoc 脚本（它们往往是最真实的 use case）。

由于篇幅限制，我们在这篇文章中重点探讨了**反馈闭环**在 Agent 开发中的意义。有机会我们可以进一步分享另一个 Agent 工程化中的重点：Context 和 Memory 的管理。

最后回到标题：Agent 不是代码生成器。它更像一个执行者，工程化也不再是“慢”的代名词——工程化是在 AI 时代让你敢让它跑起来、并且跑得不摔跤的那套护栏。

**每个人都需要成为一个合格的 Agent 管理者。**

## 附录 1：MDR：把流水线跑顺的一次实践

理论说了这么多，让我们来看一个完整的实战案例：一个新的 tick 数据读取框架（mdr）的开发过程。**这个项目从 0 到 1，完整地展示了如何在 Agent 时代进行工程化开发。**

**在这个实战中，我不会贴任何的 Prompt。**Prompt 并不是重点，重点是我们如何构建这个开发 pipeline，让任何基本合格的 Prompt 都能达成我们目标的开发需求。

实践是非常不适合在文章中展现的形式，因此作为附录简单展示一下，我会尝试在每一步注明我们这么做的目的，以及我们收获了什么。

### 让数据先跑起来：准备必要的信息获取脚本

首先，我们准备好数据存放的位置，并让 Agent 实现一个超简单的 Python 脚本来读取原始文件。这是一个 "hello world" 级别的实现，目的是确保我们能够访问和理解数据。

Why：一个合适的数据获取脚本，可以让 Agent 自动帮我们抓取很多数据的细节信息，而手工整理这些信息是非常繁琐的工作，非常消耗心力资源。

**收获：一个 Demo 脚本，可以读取任意时间段的数据，查看数据存储格式、数据类型、数据信息。**

### 编写初始文档：最小的人工准备工作

然后，我们撰写需求文档，明确以下几点：

1. 我们试图解决什么问题？（老框架的瓶颈是什么？）
2. 老框架有什么现有的问题？（性能？可维护性？API 设计？）
3. 有哪些现成的资源可以使用？
4. 数据在哪里？如何访问？

这个需求文档不需要非常详细，过于详细反而会浪费 review 的注意力，但必须清晰，且经过完善检验。它的目的是为后续的架构设计奠定基础。

**收获：一个最小的需求文档**

### 用“思考模型”打磨架构：从 high-level 到可执行 prompt

这一步是最关键的。我们与 ChatGPT Pro Thinking（一个强大的思考模型，但只能在网页上使用而非 API）进行了若干轮沟通。

首先，我们提供需求文档，让它阐述对需求的理解，并提出缺少哪些关键信息能辅助它决策。这一步很重要，因为它能帮助我们发现自己想法中的漏洞和不清晰的地方。

然后，我们补充信息，并根据自己的 sense 添加一些人为引导。比如，出于性能考虑，我们希望用 Rust 实现而不是 Python。这个引导很重要，因为它能帮助模型在多个可能的方向中做出选择。

接下来，我们要求它先做一版 high-level 的架构设计，比如拆分成哪些组件/layer，每个组件/layer 负责什么。这个版本应该是相对简洁的，能在一两页纸上表达清楚。我们进行评审、讨论、修改。

然后，我们要求它做出更细节的架构设计，比如项目的组织方式、每一层的关键类、负责维护的状态、关键 API 的 signature 和行为。这个有点像传统软件开发中的 UML。这一步产生的文档会相对较长，但非常具体。

最后，我们要求它根据它输出的架构，输出一版给 Agent（Codex）使用的 prompt。这个 prompt 会要求 Agent 创建好项目的整体框架，建好对应的文件、关键类、关键方法，但在实现中留白。这个 prompt 本身就是一个很好的 "协调指令" 的例子。

**收获：一个完善的设计文档，以 codex prompt 的方式呈现**

### 先搭框架、先让它编译：把 build 当成第一条反馈信号

进入工作目录，我们开始跟 Agent 对话。这里有一个可能有用的 trick：先选择一个你觉得维护状态良好、技术栈相似的模板项目（对于 mdr，我选择了 `apache/opendal`），把它的路径放在 prompt 的开头，告诉 Agent 可以借鉴这个 repo 的工程实践。

我猜测这个 trick 有用的原因是，LLM 学习的知识包括各个框架各个版本的内容，不同版本的 best practice 会有污染。在 context 中添加一个模板项目，可以强化 Agent 对当前版本最佳实践的认知。

然后，我们把 ChatGPT Pro Thinking 输出的 prompt 交给 Agent，等待它完成框架搭建。我们要求它编译通过，这是一个很好的 "反馈信号"——如果代码不能编译，就说明框架有问题。最后，让它根据实现生成 `README.md` 和 `AGENT.md`。（Dump Memory）

**收获：一个可以通过编译的脚手架**

### 构造一个客观事实作为正向反馈：e2e integration test

对于行情数据的获取，客观事实就是应该尊重我们已知的行情信息。我从现有的因子中捞出了一些标的的每秒 mid price 等信息，然后要求 Agent 实现一个测试，确保基于新框架读取数据计算的 mid price，与我们已知的事实完全相等。

这是一个非常具体的、可验证的需求。Agent 可以清楚地知道什么是 "正确" 的。

我还构建了一些其他的测试来覆盖 depth 和 trade 混合读取、跨标的读取等需求。这种端到端的集成测试，比传统的、经过 monkey patch 的单元测试能测到更多东西，是我更喜欢的工程实践。

**收获：使用 Ground Truth 做了一个非常强的正反馈信号**

### 让实现追着测试跑：完成核心实现

要求 Agent 填充接口中的实现，以通过上面编写的测试。然后进行简单的代码 review。这一步应该相对快速，因为框架已经搭建好了，Agent 只需要填充具体的逻辑。这一步会花费相当多的时间，但开发者需要做的事情反而非常少。因此没什么好说的。

**收获：通过 e2e integration test 的一个正确实现**

### 用快照扩展覆盖面：把行为连续性固化下来

在完成了核心实现之后，我们又可以让 Agent 继续迭代整个测试框架，为后续开发提供更多的正反馈。

比如，要求 Agent 大量生成快照测试。例如，每天每小时的订单簿快照，或者把接口的输出构建成 pandas dataframe 之后，dump `df.describe()` 的结果，并多次运行，保证结果是稳定的。这些快照测试不需要人工验证，只需要在后续的迭代中，如果快照发生变化，我们才需要人工检查。（本质上就是在铺 **continuous regression testing** 的覆盖面。）

**收获：基于当前正确行为，尽可能覆盖了所有行为的迭代连续性反馈**

### 给性能也装上闭环：benchmark + flamegraph

要求 Agent 根据当前的测试，构建一些性能测试，并且能很方便地被 `cargo flamegraph` 调用。比如读取每天前 20 分钟的数据，回放订单簿等。

这一步的目的是建立一个 "性能反馈" 的机制。Agent 可以通过运行 benchmark，看到具体的性能数据，然后做出优化决策。

**收获：构建了一个好的性能正反馈函数**

### 优化也要可回滚：收益不够就撤

这一步是最有趣的。我们要求 Agent 运行 benchmark，生成 flamegraph，并提出可能的性能优化点。

对于每一个优化点，我们要求 Agent 按照以下流程进行：

1. 尝试实现这个优化。
2. 先通过上面生成的行为测试，保证没有修改行为。
3. 再运行 benchmark，确认是否有可观的性能提升。
4. 如果有可观的性能提升，简单 review 代码，并提交。
5. **如果没有可观的性能提升，要求 Agent 回滚代码。**

为什么要回滚？因为性能优化往往会增加代码复杂度，进而增加 context 占用，必须要用在刀刃上。一个没有明显收益的优化，反而会增加项目的维护成本。

**收获：基于前面构建的正确性反馈、连续性反馈和性能反馈，自动获取了一个高性能的正确实现**

### 收尾与沉淀：把短期记忆写进仓库

最后，我们要求 Agent：

1. 生成对应的中文文档。可以给一个自己喜欢的文档模板，让它学习文风。
2. 在 `AGENT.md` 中记录重要的 knowledge，如果过长的话可以拆分一些 docs。
3. 在 `AGENT.md` 中记录哪些信息是对下游用户可见的。这很重要，因为这个信息可以用于辅助生成 `SKILL.md`。
4. 生成安装脚本/安装指南，记录在 `README.md` 中给后续的开发者看。
5. 生成 `SKILL.md`，给下游调用者的 Agent 学习库的用法。

开发者按顺序 review 并确认以上流程，然后执行发布。

**收获：沉淀这轮迭代的重要知识，同时给用户输出当前的稳定版本（客户觉得好用也是连续性反馈的重要来源），为下一次需求迭代做准备**

## 附录 2：语言选择：为什么是 Rust 相比 C++ 是 Agent 时代更好的选择？

~~这一段是纯私货~~

在讨论工程化思维时，还有一个有趣的观点值得提出：在 AI 时代下，**Rust 是比 C++ 更优的高性能编程选择**。这个判断其实跟正文的主线关系并不算强，更像是一种时代背景下的工程直觉，但我还是很想把它写下来。

**如果连编辑器都不用打开，从“工程反馈”的角度看，不同编程语言最大的区别是什么？**

答案并不是语法、生态（当然生态也很重要），而是：**语言和编译器能不能给实现者提供足够强、足够早的正反馈**。

Rust 编译器在这件事情上非常激进，可以说是有实际工程应用价值的语言里最激进的。很多人会觉得 Rust 的编译检查“限制发挥”，但从另一个角度看，它本质上是在给你一个非常明确的奖励函数：
**写错了，就别想编译通过**

这件事在 Agent 时代变得格外重要。因为 Agent 本质上并不是一个“值得信任的实现者”。你可以把它理解成一个能力极强、速度极快、但**不具备责任感、也不具备全局安全意识的超级实习生**。在这种前提下，工程系统本身就必须承担更多“兜底”的职责。

Rust 在这里的优势，并不只是“更安全”，而是它非常清晰地解决了模块与模块之间的**信任边界**问题。通过 Ownership、Borrowing 这些看起来比较难懂的规则，你可以把“哪些代码有权修改哪些内存”“哪些资源只能被消费一次”“哪些引用不可能越界”这类约束，直接编码进类型系统里。

我早年做的一个[分享里举的例子](https://rustpre.vercel.app/37)，讲的是 Rust 的 Ownership 机制如何在**必须 zero-copy、必须 inplace-write 的高性能路径**上，通过类型系统实现的架构设计，可以非常放心地让实习生、外包、或者未经充分验证的开源贡献者去写具体实现，而不用担心“一行错误代码把整个系统打崩”。

现在，这个“实习生”变成了 Agent，这个问题只会变得更尖锐。

这种场景在真实的高性能系统里并不少见。比如计算图里的表达式框架，常常会有几百个表达式实现；又比如我之前在一次分享后，和一位自动驾驶厂的 Leader 交流过，他们的规则检查框架里有**几千条 C++ 规则**，而这些规则运行在一条极其关键的性能路径上。他们非常清楚，如果能优化掉某一个关键的 copy，性能可能立刻提升 20%～30%。但现实是，他们完全不敢动。原因也很简单：只要有任何一条规则在实现时没有严格遵守内存写入的约束，就可能造成内存溢出，轻则 segfault 车机宕机，重则静默内存错误，车辆行为进入不可知状态。而编译期限制不同模块对内存的访问，从而进行 fearless 高性能优化，正是 Rust 的甜点区。

好的模块边界对 Agent 时代的开发带来的收益是什么？**可以将稀有的注意力集中在整体的框架设计上，更少关注大量的局部实现，同时也不会引入更高的风险。**

当然，这个选择成立的前提是这个项目必须是高性能实现。否则，永远选择 Python。Python 对 LLM 来说就是更专家级的技能。这可能来自于更大规模的数据集，以及更接近人类语言的表达，以及大模型技术人员本身也大量使用 Python，会更关注 Python 效果，从而可能对模型迭代产生一定作用的原因。总之，任何工具都建议实现一层 Python wrapper，作为胶水层。

## 致谢

- 感谢 ChatGPT 根据前期手稿帮忙提供文章整体组织思路，编写了初版文章（虽然最后被手工完整重写了）。
- 感谢 @tsshi 承担编辑职责，帮忙补充了文章的部分细节，润色了文章的连贯性。
- 感谢 @zqi @ycxu 周品良 作为 Beta 读者提供了大量宝贵修改意见。
- 感谢 kimi-cli 承担了最终审校工作，做了很多术语一致性/标点一致性上的改进。
