软件工程 · AI 与智能体
下一代软件工程不会发生在聊天框里
TESTER 让 FIXER 先等等
我当时有两个智能体在同一个工程里工作。TESTER 正在把散落在功能代码里的单元测试迁到独立的测试目录,并补上配置、HTTP、SQLite 和集成场景;FIXER 则在修另外几处功能问题。我不想为这点协作再开一个 worktree,也不想让两边各自发明一套测试布局。
所以我让 TESTER 把计划交给 FIXER:测试结构完成某一步之前,先不要写新的测试。FIXER 收到交接后暂停了那部分工作,先完成不重叠的功能修正,并记录自己改过哪些文件。等新布局可用,再把回归场景放到正确位置。
这段经历让我真正兴奋的不是“两个 AI 会聊天”。会发消息并不稀奇。重要的是,交接里有所有权、顺序、边界和待验证结果。TESTER 甚至拒绝为了测试方便而扩大一个生产 API,转而走真实集成路径。那才像工程:便利可以讨论,产品合同不能顺手改掉。

对话会消散,工程制品会留下。
代码多了,工程没有跟着变多
生成代码已经很便宜。需求写得差一点,智能体照样能迅速产出一大片可信的实现;架构方向错一点,它也能勤奋地把错误铺满更多文件。速度不会修正方向,只会放大方向。
我越来越确信,稀缺的部分已经从“把语法敲出来”转向另一组工作:把意图写清楚,做出可见的取舍,拆分责任,协调冲突,独立审查,收集运行证据,并在发布时保留明确的授权。代码产量可以暴涨,以上每一项仍要有人负责。
因此,我的判断很直接:聊天是很好的局部界面,却是软件工程系统的错误主抽象。问题不在聊天不够聪明,而在它把过多不同性质的状态压进了一条看起来连续的时间线。
聊天当然有用
先把最容易误解的地方说清楚:我不主张扔掉聊天。探索一个 API、解释一段陌生代码、修一个边界明确的 bug,或者让智能体运行命令并根据结果继续处理,聊天都很自然。任务短、职责单一、上下文连贯时,一条会话往往就是最低摩擦的选择。
现代编程智能体也早已不是只会补全下一行代码的输入框。它们能读取文件、调用工具、执行测试、创建子智能体,并把工作延续很多轮。Anthropic 的会话指南甚至明确建议按任务管理会话与子智能体上下文。把这些能力统称为“聊天机器人”,反而低估了它们。
但界面能完成工程动作,不代表界面本身适合保存工程事实。扳手能拧紧螺栓,我们不会因此把扳手当成装配记录。
可读的记录,不等于可恢复的状态
一条长对话很容易制造连续感:上面讨论过设计,中间改过代码,下面跑过测试,于是整件事似乎都“在这里”。可当另一个智能体接手时,它真正需要的不是滚动历史,而是明确答案:当前规格是什么?哪项决定被推翻了?哪些文件由谁拥有?哪些检查已经通过?哪个风险仍未关闭?谁有权发布?
如果这些答案只能从几百轮对话里重新推断,项目就没有可靠状态,只有一份需要考古的记录。更麻烦的是,同一句“完成了”可能指代码写完、测试通过、审查结束,也可能只指某个智能体已经停止输出。工程不能长期靠语气判断生命周期。
持久状态必须能被单独定位、比较和复核。规格应当有版本,代码应当有 diff,测试应当有结果,审查应当有 findings,发布应当有候选物、门禁与回滚记录。聊天可以解释这些对象,但不该替代它们。
上下文是工作记忆,不是项目记忆
官方文档对“上下文”给出了相当具体的范围:系统提示、对话、工具调用与输出、读取过的文件,都可能占用模型当前可见的窗口。它更像工作台,而不是档案室。工作台越大,能同时摊开的材料越多;但材料摆在上面,并不会自动变成经过归类、带版本和责任人的项目知识。
这里的区别不只是保存时间。项目知识需要身份和关系:这一条验收标准属于哪版规格,这个测试证明了哪个行为,这项审查结论针对哪个提交,这次发布授权对应哪个候选物。上下文窗口主要解决“模型现在能看到什么”,工程系统还要解决“团队以后如何知道什么是真的”。
把整个仓库和聊天都塞进更大的窗口,可能方便一次横向分析,却不会凭空生成这些关系。容量是必要工具,不是治理模型。
压缩之后,会话还在,细节未必还在
长会话总会遇到上下文转换。不同产品的实现不能混为一谈。Anthropic 明确把 /compact描述为用有损摘要替换历史,并给出过一个很诚实的例子:当后续任务方向无法预见时,一条警告可能没有进入摘要。其术语文档也说明,压缩时较早的工具输出会先被清理,再总结对话;磁盘上的项目指令可以重载,只存在于对话里的指令则可能丢失。
OpenAI 描述的是另一种机制。Responses API 的原生压缩会生成加密、节省 token 的压缩项,并保留选定的高价值早期内容;Codex agent loop 的说明同样谈到自动压缩与不透明的 compaction 项。官方资料没有说它与 Anthropic 的摘要以同样方式损失信息,我也不会替它们下这个结论。
共同点已经足够关键:界面仍显示同一项工作,下一轮执行者可用的表示却发生了转换。压缩可以维持连贯性,但连贯性不是完整性证明。真正不能丢的决定,应该离开聊天,进入可检查的工件。
工程里最危险的内容往往不是主线,而是限定主线的一句话:迁移只针对全新数据库;某个缓存必须在发布后清理;一个平台还没有完成真实安装验证;这个 API 不能为了测试便利而暴露。
摘要通常会保留“我们要做什么”,却未必知道未来哪条警告会成为关键。Anthropic 公布的遗漏示例有价值,正因为它没有假装摘要能够预测下一步。问题也不应被夸大成“压缩必然漏掉决定”;更准确的说法是,我们无法把不可丢失的工程义务押在压缩器对未来相关性的猜测上。
如果一条信息决定了实现是否可接受,就把它写进验收标准。如果它决定了架构为何如此,就写进决策记录。如果它阻止发布,就让它成为门禁。别把红线藏在聊天第 347 条,然后期待下一个执行者有考古学天赋。
模型切换是执行者切换
聊天还有另一种视觉错觉:对话没变,执行者就没变。实际上,模型切换意味着下一次请求由另一个模型处理,并使用自己的计算与缓存条件。Claude Code 的缓存说明指出,切换模型后,下一次请求会读取同一份对话,但不会命中前一个模型的提示缓存;规划与执行也可能由不同模型承担。
这不是坏事。相反,它让任务分配更有意思。一次真实工作中,我让一个 Codex 规格智能体发起第二次独立审查,由 Claude 会话接手,并把推理强度设为 xhigh。这个记录证明的是我们确实可以把提供商、上下文、强度与职责组合起来,不是 Claude 或 Codex 在所有审查任务上谁“获胜”。没有对照实验,就没有排行榜。
同理,推理强度也不是越高越专业。OpenAI 的模型指南建议在代表性工作负载上测试设置。工程上的问题应该是“这项责任需要什么能力与成本”,而不是“哪个下拉选项看起来最豪华”。
更长上下文的最强论据
反方最强的观点并不弱:如果一个模型能一次读取完整仓库、完整设计史和所有工具结果,很多人为切分会变得多余。跨模块分析、全局重构和长链路调试确实受益于更大窗口。长上下文研究也没有停止;例如 SubQ 发布了关于稀疏注意力与长上下文检索的公司技术报告。这些结果尚未在本次证据中得到独立复现,但足以提醒我们,不要把今天的限制写成永恒定律。
同时,容量不等于稳定利用率。2024 年的 《Lost in the Middle》在多文档问答和键值检索任务中发现,相关信息处在输入不同位置时,受测模型表现会明显变化,重要内容位于中间时经常更差。那是对当时模型和特定任务的结果,不是 2026 年模型的成绩单,更不是我的仓库测试。
我接受更长上下文能减少一部分切分,却不接受它能替代规格版本、所有权边界和发布授权。即使模型能记住全部讨论,“谁批准把这个候选版本推向稳定渠道”仍然不是注意力问题。
项目知识必须能离开会话生存
我把能跨会话生存的工程知识分成几类:规格记录目标、非目标、验收标准和未决问题;ADR 记录影响长期结构的取舍;Git 记录代码与历史;测试记录可执行的行为约束;审查记录把发现与具体差异关联起来;发布证据记录候选物、平台检查、授权、推广和回滚。
这些对象不保证决策正确。它们提供的是另一种更基础的能力:错误可以被定位,修改可以被比较,责任可以被追踪,下一位执行者可以从项目当前事实开始,而不是重新相信一段摘要。
持久消息同样如此。消息需要不可变内容、明确投递状态、重启后的处理规则和对模糊提交的保守策略。终端文本可以呈现过程,却不适合作为消息送达与否的唯一事实来源。界面上的动画再顺滑,也不能代替幂等性。
分解责任不是智能体时代的新发明
我们早就知道,软件应围绕容易变化的决定来划分边界。Parnas 在经典的模块分解论文中比较了不同分解方式,核心不是把文件平均分组,而是把可能变化的设计决定藏在清晰接口之后。Eric Evans 的 DDD Reference则用限界上下文说明某个模型在哪里成立,并要求显式描述上下文之间的关系。
他们当然没有提出“一项责任配一个 LLM”。从这些思想推导出智能体边界,是我的工程类比,不是历史事实。真正值得继承的原则是:边界应该服务于变化、认知和责任,而不是服务于组织图的整齐。
所以,一个智能体完全可以承担多个角色;一项任务也未必需要多个提供商。只有当独立判断有价值、写入范围会冲突、上下文需要隔离或授权必须分开时,拆分才有收益。智能体数量不是架构质量指标。
规格先把决定从聊天里捞出来
我最近审查过一份把 Task 重构为 Specification 的规格。真正推进工作的不是把名词换得更漂亮,而是把选择写实:允许改变合同;“不迁移”必须明确指全新数据库;旧的表与节点类型可以移除;acceptanceCriteria[] 取代原来的完成定义;状态、证据、输出、可追溯性、backlog 行为、canvas 呈现以及与编码智能体的关系都进入范围;代码审查还要分阶段进行。
这份规格不是一次对话的纪要。它成为后续实现可以引用、修改和审查的对象。决定改变时,diff 会显示改变了什么;实现偏离时,审查者可以指出偏离哪条合同;某项风险仍然开放时,它不会因为会话说过“明白了”就自动关闭。
规格也不该变成神谕。它可以被推翻,甚至在完成后删除,前提是需要长期保留的决定已经进入合适的工件。持久不是要求每个草稿永生,而是要求关键状态不能只靠某个上下文记得。
编码职责需要仓库事实
同一项工程里,编码智能体实现了基于 Git worktree 的工作循环:保存源分支与基线信息,支持 rebase,完成时检查工作区、合并回源分支并清理 worktree;冲突时中止并返回错误。实现随后通过了构建、类型检查、lint,以及 10 个测试文件中的 94 项测试。
这些数字只描述那次会话报告的检查,不证明一般质量,也不证明每条命令都已由本文重新执行。它们在这里的意义是:编码职责有具体输入、具体仓库差异和具体验证结果,而不是“智能体说写好了”。对应提交又给这段过程留下了独立的 Git 轨迹。
编码智能体应当能说清自己改了哪些文件、基于哪个版本、哪些检查已运行、哪些风险未覆盖。只要这些事实存在仓库和验证记录里,换一个会话继续并不可怕。真正危险的是只保留一段自信的完成摘要。
测试职责不是替实现擦屁股
TESTER 的工作边界很具体:迁移外部测试布局,补回归场景,覆盖配置、HTTP、SQLite 和集成路径,同时不改变热路径行为;另一个智能体负责 Windows 上的 DCMTK 静态运行时,因此测试方避开那片代码与 CI。这种边界看起来比“帮我多写点测试”麻烦,却让冲突变得可管理。
更有意思的是测试性取舍。为了更容易构造测试,最省事的办法可能是把内部的 Deserialize 能力暴露到生产 API。测试智能体没有这样做,而是选择真实集成路径。这个决定不能推广成“永远不要为可测试性调整 API”;它只说明,测试职责也必须尊重产品合同,不能把自己的便利伪装成架构原则。
测试的产物不只是绿色数字,还应包括覆盖边界:验证了什么,没有验证什么,依赖什么环境,哪些场景仍然等待另一方交接。否则,测试越多,误解也可能越精致。
独立审查要能推翻绿色结果
worktree 实现通过构建、类型、lint 和 94 项测试之后,我仍要求做代码审查。审查报告找到了两个中等问题和两个低等级问题。随后我拒绝了重复状态,选择让 Git 成为事实来源,并授权进一步简化后再审查。
这不能推出“独立审查总能发现 bug”,也不能说明测试不重要。它说明两类检查回答不同问题。自动化验证的是我们已经编码进去的约束;审查还可以追问:状态为什么存在两份?哪个才是权威?失败后能否恢复?这个设计让下一次变化更容易还是更难?
如果审查者只拿到实现者的聊天摘要,独立性会很薄。更好的输入是规格、代码 diff、测试结果和已知限制。审查结论也应留下严重级别与证据,让人可以接受、拒绝或要求修正,而不是把“reviewed”当成新的绿色徽章。
专业化是资源分配,不是模型信仰
规格、编码、测试、审查和发布需要的工作形态不同。规格审查偏重矛盾、边界与长期后果;编码需要紧贴仓库和工具反馈;测试需要系统地寻找失败路径;独立审查需要新鲜上下文;发布需要保守、可恢复并服从授权。把它们全部塞给同一个无限会话,并不天然更连贯。
我会按责任选择提供商、模型、推理强度、工具权限和上下文,而不是先选“最强模型”,再让所有问题长得像它喜欢解决的样子。前面那次 Claude xhigh 的第二审查是一个实际分配案例,仅此而已。官方厂商对模型分工的建议可以作为起点,但它们是产品指南,不是中立基准。
专业化也不意味着每一步都追求最高计算量。简单的文件核对可能适合更轻的执行者;架构审查可能值得更深推理;高风险发布则可能更需要确定性工具和人类确认,而不是更会写解释的模型。选择必须回到代表性任务、失败成本和可验证输出。
编排最危险的时候,是它开始编事实
我也踩过一个很典型的坑:试图从不同编程智能体的信号里推断统一状态。实际行为很快打脸。Claude Code 正在请求权限,界面仍显示 running;Codex fork 会话后长期停在 ATTENTION;任务完成也没有可靠的一次性通知。
我们最初把它当显示问题,后来发现这是建模错误。系统声称知道提供商内部在做什么,实际上只观察到终端和 UI 的零散迹象。我最终要求整份状态检测规格重做,删除那些无法确信的推断,只保留 PTY/UI 能证明的窄状态,再改用 stopped、running 等可观测事实。
这段失败比一张漂亮的多智能体架构图更有价值。编排层一旦制造虚假确定性,就会把不可靠信号升级成工作流事实。最诚实的状态有时是“未知”。软件可以少一个炫目的徽标,别多一个假的真相。
发布不是编码结束后的自动动作
发布是独立责任,因为它把内部判断变成用户后果。一次工作中,功能被报告为完成并测试后,我允许生成包,但明确禁止更新站点:Windows 和 Linux 还要继续验证。后来又发现关键 bug,我及时中止了候选发布。
另一次稳定版本已经需要下架。安装环境复现出依赖缺失,进程在握手前退出,上层还把失败掩盖成 20 秒超时。我授权以可逆方式从 stable/latest 渠道移除错误版本,同时尽量保留 tag、工件和证据。会话还记录了公共端点检查,以及缓存清理权限受限的事实。
这不是“自动发布很安全”的成功故事。它只证明了几次具体的人类决定:暂缓推广、中止、回滚,以及在权限不足时保留限制。一个可靠的发布系统必须区分生成候选物、验证平台、批准推广、实际发布和回滚;其中任何一步都不该因为聊天里出现“100% 完成”就自动越权。
Asaph 的迭代账单
另一个仓库给了我反方向的提醒。Asaph 的早期原型有 50 个受 Git 跟踪的 TypeScript/TSX 文件,没有受跟踪的测试或规格文件;抽查的 bands 和 volunteers API 路由直接导入 Prisma,在路由处理器里解析 request.json()、使用内联请求形状并调用 ORM。
这组事实不能证明应用有缺陷,也不能证明它由智能体独立写成。直接访问 ORM、内联类型和零初始测试,各自在小原型里都可能是合理选择。真正值得看的是随后几天的变化:一次 Prisma 到 Mikro-ORM 的迁移带来 9,175 行新增与 2,401 行删除;之后又有 4,388 行新增的集成测试与服务重构;再后来加入应用控制器、公共 DTO、更薄的适配器和更广的测试,改动仍有数千行。
我把它看成一次有边界的迭代成本案例,不是对“vibe coding”的普遍定罪。原型可以买到学习速度,但结构性决定迟早要结账。关键不是拒绝原型,而是知道何时把发现沉淀为边界、合同和测试,免得每次重构都从聊天记忆重新开始。
最强反驳:有时编排层真的太重
如果只有一个工程师、一个职责清楚的小改动、一个连贯会话,而且结果容易测试和撤销,那么引入规格状态机、多个智能体、消息协议、审查门禁和发布清单,很可能是在给螺丝刀修机场。更多持久状态意味着更多同步逻辑、恢复路径和权限设计;更多角色意味着更多等待与误解。
这个反驳成立。我不建议为了“多智能体”而拆分任务,也不把长聊天本身视为失败。一个会话能够可靠完成的工作,就让它完成。大窗口能明显改善全局检查时,就用大窗口。工程成熟度不是流程数量。
边界应在风险出现的地方生长:决定必须跨上下文保存时,用持久规格;两个写入者可能互相覆盖时,明确所有权和交接;结果需要不受实现者叙述影响的判断时,引入独立审查;行为会影响真实用户时,把发布授权单独留下。编排的收益必须从失败处理里证明,不能从图里数箭头。
人可以委托工作,不能委托责任
智能体已经能够执行相当多的工程工作:读规格、改代码、跑测试、审查差异、准备发布操作。即便未来模型能力继续增长,产品目标、风险承受度和对用户的责任也不会因为一句“交给 AI”自动找到新主人。关于 AGI 的时间表,我没有证据,也不需要靠它支撑今天的工程决定。
人的角色不必退回逐行敲代码。更重要的工作是确定哪些事实必须持久,哪些职责需要隔离,什么证据足以推进,哪些风险可以接受,以及何时必须停下。授权不是点一下“Approve”这么简单;它意味着理解候选物、限制和后果,并愿意对决定负责。
真正的下一代软件工程,不会被困在一条越来越长的聊天记录里。它会让对话保持轻便,让工程状态保持耐久,让智能体可以更换,让决定可以复核,让发布可以撤回,也让责任始终有明确归属。
我正在用 Agent Kavor 尝试把这层工程系统真正做出来。
这篇文章给你带来了什么?
可以选择多个选项,也可以随时取消。

评论
正在加载评论…