AI生成的技术债务:编码代理的架构垃圾回收
摘要
这篇文章强调了编码代理如何在软件系统中引入技术债务,并提出了使用诸如ArchUnitPython等工具来强制执行架构规则,以防止结构性问题。
暂无内容
查看缓存全文
缓存时间: 2026/09/12 22:45
# AI生成的技术债务:编码代理的架构垃圾回收
来源:https://lukasniessen.medium.com/ai-generated-technical-debt-architecture-garbage-collection-for-coding-agents-aefddb73230e
Lukas Niessen (https://lukasniessen.medium.com/?source=post_page---byline--aefddb73230e-----------------------------------------)
按回车或点击查看完整图片
编码代理实现了功能。测试通过。拉取请求看起来很干净。你合并了它。
但该代理也复制了一个现有的辅助函数,直接从API路由导入了数据库适配器,并向一个已经负担过重的类添加了另一个职责。
功能正常工作。代码库变得更糟了。
这是2026年一个有趣的软件工程问题。生成可运行的代码变得廉价,但让系统在人类和代理并行生成变更时保持一致性则并非如此。
简而言之:
> 编码代理需要一个防止新增结构性债务的关卡,以及一个持续移除已通过关卡的债务的垃圾回收器。
你需要测试来确定性地强制执行这一点。对于Python项目,[ArchUnitPython](https://lukasniessen.com/blog/169-archunit-python)是构建这两个循环的实用方法。代理不再仅仅在`AGENTS.md`中阅读“请遵守整洁架构”,而是会收到一个失败的测试,明确指出它必须移除的依赖项。
## 研究正在迎头赶上
2026年一篇名为《AI繁荣背后的债务》的预印本论文 (https://arxiv.org/abs/2603.28592) 分析了来自6,299个公共GitHub仓库的302,579次明确署名为AI生成的提交。作者们比较了每个提交前后,针对生产环境Python、JavaScript和TypeScript代码的静态分析结果。
他们报告指出:
- 引入了484,366个问题,其中89.3%是代码异味
- 每个被分析的助手超过15%的提交至少引入了一个问题
- 22.7%被追踪的问题在仓库的最新分析修订版中仍然存在
另一篇论文《更多代码,更少复用》 (https://arxiv.org/abs/2601.21276) 被MSR 2026接收。其完整数据集包含3,858个Python拉取请求。对617个crewAI拉取请求的详细冗余分析发现,代理变更的平均最大冗余度分数为`0.2867`,而人类变更为`0.1532`:高出近`1.87倍`。然而,审查者对代理拉取请求表达了更多中性或积极的情绪。
这种组合令人担忧。丑陋的代码会引人注意,而看似合理、格式良好、冗余的代码却容易通过审查。
结论是:
> 通过测试和外观悦目的差异并不能告诉我们生成的代码是否可维护。
GitHub在其审查代理拉取请求的指南 (https://github.blog/ai-and-ml/generative-ai/agent-pull-requests-are-everywhere-heres-how-to-review-them/) 中提出了类似观点:代理常常复制附近的模式,而不检查仓库是否已经包含可复用的解决方案。一旦重复项被合并,未来的代理也可能复制它。
## 为何绿灯测试会错过架构债务
代理并非天生就想制造混乱代码。人类也会产生同样的问题。区别在于优化目标和速率。
假设任务是:
```
添加一个返回客户订阅的端点。所有测试必须通过。
```
在路由处理函数中直接查询数据库可以满足所有可见的需求。但真实需求可能还包括:
```
API应依赖应用服务,而非数据库适配器
授权逻辑应被复用
领域代码应保持框架无关性
不应创建第二个订阅查询点
```
单元测试回答的是局部问题:功能行为是否正确?而架构是一个全局问题:这个依赖是否被允许,此变更是否创建了循环,以及某个模块是否积累了不相关的职责?
如果这些约束只存在于架构师的脑中,那么对于代理来说它们就不存在。指令有所帮助,但它们改变的是遵守的概率。而测试改变的是成功的定义。
## 架构垃圾回收
这个术语源自OpenAI的一份关于构建由代理生成代码产品的工程报告 (https://openai.com/index/harness-engineering/)。该团队最初每周花一天时间清理AI生成的杂乱代码。后来,他们将机械的“黄金原则”编码化,并运行周期性任务来扫描偏差,开启小型重构拉取请求。
Thoughtworks在2026年4月的技术雷达 (https://www.thoughtworks.com/content/dam/thoughtworks/documents/radar/2026/04/tr_technology_radar_vol_34_en.pdf) 中描述了相同的模式:确定性分析发现结构问题,大语言模型可以评估或修复它们,独立的验证循环检查结果。
操作模型很简单:
```
架构不变式
↓
确定性扫描
↓
一个小违规
↓
代理提议一个重构
↓
行为 + 架构测试
↓
人类审查并合并
```
这不是季度性的重写。这是持续的小增量维护。
我会使用三个循环:
1. 准入控制:在开发期间和每个拉取请求上运行快速的架构检查。
2. 定期收集:每日或每周的代理选择一个违规项,并开启一个专注的重构拉取请求。
3. 一个棘轮机制:现有系统从其当前质量水平开始,随着债务的清除逐步收紧阈值。
定期收集器不应创建一个20,000行的“修复架构”变更。每个拉取请求处理一个循环、一个过大的类或一个重复的抽象,会更容易审查和回滚。
## 用ArchUnitPython使规则可执行
假设一个Python项目使用此结构:
```
src/shop/
├── domain/ # 业务规则
├── application/ # 用例和端口
├── adapters/ # 数据库和外部系统实现
└── api/ # FastAPI路由
```
API可以调用应用层,应用层可以依赖领域层,适配器实现更深层的端口。API不应直接跳转到数据库适配器。
使用[ArchUnitPython](https://github.com/LukasNiessen/ArchUnitPython),这变成一个pytest测试:
```python
from archunitpython import assert_passes, metrics, project_files, project_layers
def test_layer_dependencies_point_inward():
rule = (
project_layers("src/")
.layer("domain").defined_by_folder("**/domain/**")
.layer("application").defined_by_folder("**/application/**")
.layer("adapters").defined_by_folder("**/adapters/**")
.layer("api").defined_by_folder("**/api/**")
.where_layer("domain").may_only_depend_on_layers()
.where_layer("application").may_only_depend_on_layers("domain")
.where_layer("adapters").may_only_depend_on_layers(
"application", "domain"
)
.where_layer("api").may_only_depend_on_layers("application")
)
assert_passes(rule)
def test_project_has_no_dependency_cycles():
assert_passes(project_files("src/").should().have_no_cycles())
def test_files_do_not_keep_growing():
rule = metrics("src/").count().lines_of_code().should_be_below(600)
assert_passes(rule)
```
现在,一个代理添加了这个快捷方式:
```python
# src/shop/api/subscriptions.py
from shop.adapters.postgres.subscription_repository import SubscriptionRepository
```
端点测试可能通过,但架构测试失败,因为`api`可能依赖`application`,而非`adapters`。代理可以引入正确的应用端口,重新运行pytest,并在人类审查拉取请求之前自我修复。
不要盲目复制`600`行阈值。指标是传感器,而非质量分数。测量可比的文件,从接近当前最大值开始,并随着清理工作的完成降低上限。
此外,ArchUnitPython无法检测所有类型的债务。依赖方向、循环、外部导入、命名和大小是很好的确定性规则。使用不同语法编写的语义等效代码需要克隆检测器或大语言模型来寻找候选项,然后由人类审查并进行行为测试。
## 将检查放入代理的循环中
CI集成很简单,因为这些只是普通的pytest测试:
```yaml
- name: Architecture fitness functions
run: python -m pytest tests/test_architecture.py -q
```
告诉编码代理在宣布成功之前运行该命令。如果失败,代理应修复生产代码,重新运行专注的行为测试,然后重新运行架构测试套件。
一条规则至关重要:代理不得削弱、删除或跳过失败的防护栏。架构测试、CI工作流、阈值和忽略列表应需要明确的人类批准,例如通过`CODEOWNERS`。否则,一个为绿灯CI优化的代理可能会移动验证器,而不是改进实现。
## 在遗留项目中起步
遗留系统在第一次扫描时可能会产生数百个违规项。一个永远保持红色状态的测试套件毫无用处。
从一个痛苦的边界开始:
```
问题:API处理程序直接导入仓库。
规则:API可以依赖应用层,绝不能依赖适配器。
初始范围:一个已经干净的模块。
下一步:修复一个相邻模块并扩展规则。
```
对于指标,将第一个上限设置在接近当前最大值的位置,并随着清理工作将其向下调整。保持例外情况范围狭窄、有文档记录、有负责人且有时间限制。对`legacy/`的广泛永久忽略不是迁移计划;它是另一个没有负责人的架构。
跟踪新违规项、已移除的违规项、依赖循环、合并后返工、拉取请求大小和审查轮次。不要优化生成的代码行数。这个指标奖励的恰恰是我们试图控制的行为。
## 总结
AI生成的技术债务并非债务的新种类。我们已经有了重复逻辑、循环、巨型类和层违规。改变的是生产率。
实践模型是:
```
指令解释架构
架构测试强制执行客观边界
计划代理提议小型清理
独立测试验证结果
人类保留判断力并控制规则
```
这就是架构垃圾回收:在拉取请求边界阻止已知债务,并持续收集仍会通过的债务。
相似文章
AI代理是否正在制造一种新型技术债务?
探讨了因部署和维护AI代理而产生的特定技术债务概念,为软件工程提出了新的挑战。
AI 编码工具正在以团队尚未意识到的速度产生技术债务,而上下文缺失是罪魁祸首
文章认为,AI 编码工具因忽视既定的组织规范,在企业代码库中产生了隐蔽的技术债务。这一问题需要通过增强上下文感知能力来解决,而不仅仅依靠提升模型质量。
管理智能体AI系统中的技术债务
本文介绍了智能体技术债务和随机税的概念,定义了结合随机模型与工具使用及工作流的智能体AI系统特有的新负债和运营成本,并提出了轻量级的治理控制措施。
我的团队一天内上线了一个能处理技术债务的AI代理。最难的部分不是代码,而是把问题定义得足够清晰,让代理能够接手执行。
一个团队构建了AI代理来自动修复技术债务:扫描代码库、自动提交PR。他们发现最困难的是精确定义问题,此外还讨论了多个代理在同一代码库上运行时的冲突问题以及设置防护栏的必要性。
@bibryam: 使用代理,保持主导权. https://devindickerson.dev/posts/using-agents-keeping-agency/…
一篇博客文章警告,AI 编码代理通常会默认选择流行但不适用的技术,导致技术债务,并敦促开发者在架构决策中保持主导权。