当AI辅助开发失败时:一份技术事后分析
我要求AI一次性构建完整的系统:一个健壮的asyncio调度器、动态参数调整的任务执行、自我改进任务生成,以及失败任务的人工工单系统。AI快速交付了可工作的代码。问题只在我尝试集成时才出现。
系统崩溃了。不是因为新颖的缺陷,而是经典的软件工程问题被无约束的开发过程放大:范围膨胀、集成未审查、紧密耦合、技术债务嵌入基础。
为何无约束的AI开发会失败
缺乏架构审查的范围膨胀
AI没有业务背景或架构直觉来说"不"。它是一个强大的实现引擎,优化用来执行请求。通过一次性要求一切而不定义集成点或架构约束,我无意中引导它解决一个尚不存在的未来问题,放弃了对稳定调度器的直接需求。
"智能"功能被强行附加到从未经受压力测试的核心上。我在把AI当作自主开发者,期望从功能清单中获得一致性。同事如果收到同样的需求,会提出异议、提议分阶段方法,并对复杂性表示担忧。AI直接执行了。
事件循环冲突
当我尝试运行集成系统时,它崩溃了。根本原因具有范例性:不同模块在隔离开发中试图管理同一个asyncio事件循环。
核心调度器使用一种模式初始化。然后工单系统,在独立开发中,用asyncio.run()处理失败任务:
问题代码片段1:冲突的事件循环
# 在scheduler_core.py中,由一个提示生成
import asyncio
from apscheduler.schedulers.asyncio import AsyncIOScheduler
class MainScheduler:
def __init__(self):
self.scheduler = AsyncIOScheduler()
def run(self):
self.scheduler.start()
# 这个调用会永远阻塞,运行循环
asyncio.get_event_loop().run_forever()
# 在ticketing_system.py中,由另一个提示生成
import asyncio
class TicketingSystem:
async def process_ticket(self, ticket_data):
# ... 逻辑 ...
print("处理工单")
def handle_failed_task(self, task_info):
# 这是反模式!它试图运行一个新循环
asyncio.run(self.process_ticket(task_info))
当调度器在执行中调用handle_failed_task时,代码崩溃并报RuntimeError: This event loop is already running。工单系统的开发者(AI)看到一个本地问题——"我需要运行异步函数"——应用了标准解决方案,却没有理解全局背景:它是一个已在运行的事件循环的一部分。
第二个架构不匹配涉及apscheduler的CronTrigger。它的阻塞实现和独立线程模型与我设想的异步设计冲突。结果:难以隔离的时序错误和竞态条件。
紧密耦合与级联失败
系统变成了一个单体,模块对彼此的内部有深层依赖。自我改进任务模块直接依赖工单系统的数据结构。主调度器知道任务执行逻辑的内部细节。
概念问题:紧密耦合
# 之前:依赖关系的混乱
class MainScheduler:
def __init__(self):
# 调度器直接实例化其"智能"组件
self.improver = AutoTaskImprover()
self.ticketer = TicketingSystem()
def _execute_task(self, task):
result = task.run()
if not result.success:
# 直接调用另一个模块的实现
new_script = self.improver.analyze_and_suggest_fix(task.script, result.error)
if new_script:
task.update_script(new_script)
else:
# 另一个直接的深度调用
self.ticketer.handle_failed_task(task.info)
一个组件的失败会在整个系统中级联。调试变成不可能,因为错误不在其表现的地方发生。系统不是合作模块的集合;它是一台脆弱的机器。
放弃架构责任
我放弃了架构师的角色。我指定构建什么但没有指定如何构建或如何集成。没有每一步的手动代码审查和集成测试,我对不断积累的架构腐败是盲目的。
恢复和重新定位人机关系
从这次失败中重建揭示了一个清晰的有效人机协作框架。
从稳定核心开始
修复不是改进现有系统,而是放弃它,重新开始,目标单一明确:一个坚如磐石的、简单的异步调度器。没有智能功能。没有自我改进。只是稳定的、经过压力测试的基础。
只有在这个核心被构建、测试并验证可靠后,我才一次添加一个功能。每个新功能成为一个独立的、可选的模块,而非核心组件。
修复:模块化、可插拔架构
# 之后:使用依赖注入的清洁、解耦设计
# --- 核心调度器(对"智能"功能一无所知)---
class MainScheduler:
def __init__(self, plugins=None):
self.plugins = plugins or []
def _execute_task(self, task):
result = task.run()
if not result.success:
# 核心只发布事件,它不知道消费者
self.publish_event('task_failed', task=task, result=result)
def publish_event(self, event_type, **kwargs):
for plugin in self.plugins:
if hasattr(plugin, f"on_{event_type}"):
getattr(plugin, f"on_{event_type}")(**kwargs)
# --- 可选插件 ---
class AutoImprovementPlugin:
def on_task_failed(self, task, result):
# 改进任务的逻辑现在被隔离在这里
print(f"插件:分析任务失败 {task.id}")
# ...
# --- 主应用程序连接 ---
core_scheduler = MainScheduler(plugins=[AutoImprovementPlugin()])
# 现在智能功能是可选插件,而不是核心依赖
这保持核心清洁,允许功能被交换、升级或禁用而不影响系统其余部分。
人类必须架构和审查
我把角色从"项目经理"改为"首席架构师和高级开发者"。新工作流:
- 定义一个小的、隔离的任务(如"创建一个插件记录任务失败到JSON文件")
- AI生成代码
- 我审查每一行。我检查反模式、架构不匹配和隐含假设。
- 我自己重构和集成。我将其连接到主应用,确保遵循既定架构。
- 我编写集成测试并提交
这个以人为中心的审查循环是必需的。它让人类控制架构决策和质量标准。
执行架构约束
asyncio问题通过执行一条规则解决:只有一个事件循环,由应用入口点管理。模块和插件绝不能调用asyncio.run()或loop.run_forever()。它们暴露主循环可以等待的async函数。
修复:单一、统一的事件循环
# 在插件文件中(例如,ticketing_plugin.py)
class TicketingPlugin:
async def on_task_failed(self, task, result):
# 这个函数现在是异步的,期望被await
await self.create_ticket(task.info)
async def create_ticket(self, info):
print(f"为 {info} 创建工单")
# ... await 异步I/O操作 ...
await asyncio.sleep(0.1)
# 在主应用程序入口点
async def main():
# 插件现在设计为被await
ticketing_plugin = TicketingPlugin()
scheduler = MainScheduler(plugins=[ticketing_plugin])
# 调度器的`publish_event`需要是异步的
# 并await插件调用
# ... 启动逻辑 ...
await scheduler.run() # 主run函数现在是可等待的
if __name__ == "__main__":
# 运行事件循环的唯一地方
asyncio.run(main())
这个原则——全局执行简单性——必须来自人类。为本地目标优化的AI可能不会选择全局最简方案。
经验教训:人类作为机长
AI开发工具是强大的副驾驶,不是自主机长。它们能以超人速度执行复杂指令,处理大量技术细节。但人类开发者必须保持指挥:负责架构、代码审查、集成和最终方向。
AI的承诺是真实的。它需要新的纪律:抵抗让它无监督运行的诱惑。相反,严格指导它、质疑假设,将其输出与仅有人类架构师才能提供的前瞻性相融合。
通过将战略性人类监督与AI的战术能力配对,你避免飞入积累复杂性的风暴,反而构建既非凡又可维护的软件。
真正有效的做法
- 从稳定基础开始,不是功能清单。构建核心功能,彻底测试,然后添加复杂性。
- 每一行AI生成的代码都需要人工架构审查。不是语法检查——架构审查。这是否合适?它是否不必要地耦合?它做出了什么假设?
- 增量集成。一次添加一个功能,测试集成,提交。这能尽早捕获架构冲突。
- 保持功能可选和松散耦合。模块化系统可维护;单体系统脆弱。
- 人类架构师控制决策。AI执行。人类决定范围、边界和权衡。