AI多Agent协作系统实战(三十一):为了省token,我把1613行代码拆成了7个文件
2026/8/24 13:09:02 网站建设 项目流程

系列第31篇 | 为了给一个71KB的单文件"减肥",我用AST写了拆楼机,拆完还验证它没散架

背景:一个"不该拆"的文件

我们的任务监控脚本task_monitor.py,是个1613行的庞然大物。

它干了什么?超时检测、任务重试、agent恢复、报告生成、DB同步、健康检查——19个函数,七个职能,全塞在一个文件里。

这种文件有个经典特征:谁都不敢动它。改一行,怕炸;加功能,找不到位置;想测试,无从下手。它就像一栋住了七户人家的老楼,每户的墙都打通了,你想装修自己家,得先问问楼上楼下答不答应。

唯有大刀阔斧重构拆解,理顺层层交织的依赖关系,厘清各模块边界,才能打破这份互相牵绊的僵局。为了代码跑得更顺,拆完还能收获一个维护性更好的代码库。

第一步:给1613行做个"人口普查"

拆之前,我写了个AST(抽象语法树)分析脚本,把每个函数的行数、职责、依赖关系全摸清楚:

check_replies 29行 - 读inbox回复 diagnose_and_fix_timeout 149行 - 超时诊断修复 report_to_daxia 362行 ← 最大的一个 sync_tasks_db 114行 - DB同步 check_and_retry 301行 ← 第二大 auto_pause_if_idle 178行 - 空闲暂停 ...

4个大函数占了快2/3的代码量:report_to_daxia(362行)、check_and_retry(301行)、auto_pause_if_idle(178行)、diagnose_and_fix_timeout(149行)。

更关键的是依赖分析——每个函数用了哪些全局变量、调用了哪些兄弟函数:

report_to_daxia: 全局=[REVIEW_SCRIPT, SYNC_DIR, TASKS_DB] 调用=[diagnose_and_fix_timeout] check_and_retry: 全局=[AGENT_IDS, AGENT_SESSION_DIRS, SCRIPT_DIR] 调用=[get_retry_config, get_task_stage]

有了这张"人口普查表",怎么拆就一目了然了。

第二步:按职责域拆分

拆分原则很简单:按职责域分,不是按大小分。我把19个函数分成7个模块:

task_monitor.py(薄壳,3KB) ├── task_monitor_common.py 常量+公共加载(14个全局常量) ├── task_monitor_sync.py DB同步 ├── task_monitor_timeout.py 超时检测/诊断 ├── task_monitor_retry.py 重试/恢复核心 ├── task_monitor_reports.py 报告/通知/复核 ├── task_monitor_pause.py 空闲暂停 └── task_monitor_health.py 健康检查

关键设计——薄壳模式

原来的task_monitor.py不删除,而是变成一个"薄壳":

fromtask_monitor_commonimport*fromtask_monitor_syncimport*fromtask_monitor_timeoutimport*fromtask_monitor_retryimport*fromtask_monitor_reportsimport*fromtask_monitor_pauseimport*fromtask_monitor_healthimport*defmain():...# 主流程保留在薄壳if__name__=="__main__":main()

这样做的妙处是零破坏

  • 原来的调用方式python3 task_monitor.py不变
  • 其他模块import task_monitor依然有效(薄壳聚合导出所有函数)
  • 任务监控脚本每分钟被cron调用,不需要改任何配置

第三步:用代码拆代码,而不是手抄

拆分19个函数,如果手动复制粘贴,一定会出错——函数体几百行,粘错一个缩进就是语法错误。

我的做法是用AST自动提取函数源码

importastwithopen('task_monitor.py')asf:tree=ast.parse(f.read())fornodeintree.body:ifisinstance(node,ast.FunctionDef):seg=ast.get_source_segment(source,node)# 精确提取函数源码func_map[node.name]=seg

然后按模块清单拼接生成7个新文件。全程不手抄一行代码,从源头杜绝复制错误。

拆完立刻py_compile验证语法——7个模块全部通过。

第四步:验证"拆完没散架"

光语法通过不够,得证明功能没变。我做了三层验证:

1. 聚合导出完整性——薄壳要能导出全部19个函数:

importtask_monitor funcs=['check_replies','check_and_retry','report_to_daxia',...]missing=[fforfinfuncsifnothasattr(task_monitor,f)]# 结果: 15/15 ✅ (中途发现漏了check_idle_and_pause,补上)

2. 真实运行——跑一次任务监控脚本:

⏸️ 空闲超30分钟,已暂停心跳cron ✅ 今日25个任务已全部完成 ✅ 已即时唤醒小虾处理任务

功能正常!

**拆楼的目的达成了。** ## 顺带:review.py 也拆了 尝到甜头后,我顺手把同样超限的 `review.py`(2155行/88KB)也拆了:

review.py(薄壳)
├── review_common.py 常量
├── review_checks.py 代码/文件/CSS验证(34KB)
├── review_screenshot.py 截图/视觉分析(38KB)
├── review_db.py DB审计
└── review_task.py 任务复核/主入口

最大模块38KB,也全部加密成功。**至此,所有核心脚本都跨过了63KB的门槛。** ## 经验总结 1. **大文件不是"不敢拆",是"不知道怎么拆"**——AST做依赖分析,一张表看清函数归属,拆分就有据可依 2. **薄壳模式=零破坏重构**——原文件名保留、聚合导出、调用方无感,这是风险最低的拆分方式 3. **用代码拆代码,别手抄**——AST自动提取函数体,杜绝复制错误,拆1000行也不怕 4. **工具限制是最好的重构理由**——"为了加密"这个动机,让拖延了很久的拆分一次性落地 5. **验证三层缺一不可**——语法、导出完整性、真实运行,少一层都可能在线上炸 > 71KB的巨石,拆成7块积木,不是为了好看,是为了让工具能咬得动它。而拆完之后你才发现:不是工具太弱,是代码太胖。

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

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

立即咨询