WeClaw_65_熵管理:AI系统的垃圾回收机制
第四季系列文章第 4 篇(总第 65 篇) - Entropy Manager · 后台清理 · 文档一致性 · 死代码扫描 · 技能花园维护
📚 专栏信息
《从零到一构建跨平台 AI 助手:WeClaw 实战指南》专栏 · 第四季
专栏定位:面向开发者和技术决策者的实战专栏,用真实案例和完整代码带你理解如何构建生产级 AI 应用
本文深入探讨 Harness Engineering 七大差距中的 G3——熵管理(Garbage Collection)。软件系统在使用过程中不可避免地产生"熵增":过时的文档、冗余的技能、累积的日志、未引用的代码。本文设计 EntropyManager——一个后台低优先级运行的 Agent,在系统空闲时自动执行四类清理任务:死代码扫描、文档一致性检查、技能花园维护、旧轨迹清理。
👨💻 作者与项目
作者简介:翁勇刚 WENG YONGGANG 新概念龙虾-WeClaw 开发团队负责人,一群专注于跨平台 AI 应用的实践者 理念:"再复杂的技术,也能用代码讲清楚"
- 💻 项目地址:https://github.com/wyg5208/weclaw.git
- 🌐 官网地址:https://weclaw.link
- 📝 作者 CSDN:https://blog.csdn.net/yweng18
- ⭐ 欢迎 Star⭐、Fork🍴、贡献代码🤝
📝 摘要
本文结构概览: 从软件系统的"熵增定律"出发——任何运行中的系统都会不可逆地产生信息熵增:文档与代码不一致、技能库膨胀、日志文件堆积、死代码残留。然后设计 EntropyManager:一个以后台 asyncio.Task 运行的低优先级 Agent,监听系统空闲信号(最近 5 分钟无用户请求),执行四类清理任务。重点讲解调度策略(空闲触发 + 优雅停止)、每类检查的实现思路,以及如何复用 WeClaw 现有的 AgentPool._cleanup_loop 调度模式。
核心问题: WeClaw 的 Curator 已经实现了技能自动沉淀和维护(813行),但系统的其他维度——文档一致性、死代码扫描、旧轨迹清理——尚无自动化机制。随着系统运行时间增长,这些"熵"会逐渐降低 Agent 的执行质量。
关键成果:
- 设计了 EntropyManager 的四类检查任务
- 空闲触发 + 优雅停止的调度策略
- 每个检查项 <500ms,对主对话零干扰
适合读者:关注系统长期维护和自动化的开发者、运维工程师
阅读时长:约 12 分钟
关键词:Entropy Manager、垃圾回收、后台调度、文档一致性、死代码扫描、asyncio.Task
一、软件熵增定律
1.1 什么是"熵"?
在信息论中,"熵"衡量系统的不确定性。在软件工程中,我们借用这个概念来描述系统运行过程中产生的信息混乱:
| 熵增类型 | 表现 | 影响 |
|---|---|---|
| 文档漂移 | README 说的接口和代码实际不一致 | Agent 依赖过时文档做决策 |
| 技能膨胀 | Curator 自动沉淀的技能越来越多,大量重复或过时 | 检索效率下降,噪音增加 |
| 轨迹堆积 | TaskTrace 日志文件持续增长 | 磁盘空间、检索速度 |
| 死代码残留 | 重构后旧函数未被清理 | Agent 可能引用不存在的代码 |
1.2 为什么 Agent 系统特别需要熵管理?
传统软件的熵增主要影响可维护性——开发者读代码时感到困惑。但 Agent 系统的熵增直接影响运行时质量:
Agent 读取 README → README 说的接口已过时 → Agent 生成了错误代码 → 用户困惑
Agent 不像人类开发者能"意识到文档可能过时"。它信任上下文中的所有信息。如果上下文中有 30% 是过时的,Agent 的输出质量就会下降 30%。
1.3 现状:Curator 的覆盖范围
WeClaw 的 Curator(813行)已经实现了技能层面的熵管理:
- 对话→技能自动沉淀
- 周期性维护(相似技能合并、过时技能清理)
- 技能使用频率统计
但其他三个维度——文档一致性、死代码扫描、轨迹清理——尚无自动化机制。
二、EntropyManager 设计
2.1 核心架构
class EntropyManager:
"""后台熵管理 — 定期清理系统熵增。"""
CHECKS = {
"dead_code_scan": _scan_dead_code,
"doc_consistency": _check_doc_consistency,
"skill_garden": _tend_skill_garden,
"trace_cleanup": _cleanup_old_traces,
}
def __init__(self, event_bus: EventBus):
self._event_bus = event_bus
self._stop_event = asyncio.Event()
self._running_task: asyncio.Task | None = None
self._last_user_activity = time.time()
async def start(self):
"""启动后台调度循环。"""
self._running_task = asyncio.create_task(self._schedule_loop())
async def stop(self):
"""优雅停止。设置停止信号并等待当前周期完成。"""
self._stop_event.set()
if self._running_task:
await self._running_task
async def run_cycle(self):
"""执行一轮熵管理(在低负载时触发)。"""
2.2 调度策略
EntropyManager 的调度遵循三个原则:
原则1:空闲触发
async def _schedule_loop(self):
while not self._stop_event.is_set():
# 等待 60 秒
try:
await asyncio.wait_for(
self._stop_event.wait(), timeout=60
)
break # 收到停止信号
except asyncio.TimeoutError:
pass
# 检查是否空闲(最近 5 分钟无用户请求)
idle_time = time.time() - self._last_user_activity
if idle_time < 300: # 5 分钟
continue
# 执行一轮清理
await self.run_cycle()
原则2:低优先级 EntropyManager 的所有操作都在 asyncio.Task 中运行,不会阻塞主事件循环。即使清理任务耗时较长,用户对话也不会受到影响。
原则3:优雅停止
当应用关闭时(gui_app.py 的 closeEvent),调用 await entropy_manager.stop(),等待当前清理周期完成后才退出——避免清理到一半被中断导致数据不一致。
2.3 空闲检测
# 监听 EventBus 的 AGENT_RESPONSE 事件,更新最后活动时间
class EntropyManager:
def __init__(self, event_bus: EventBus):
# ...
# 订阅用户活动事件
event_bus.subscribe(EventType.AGENT_RESPONSE, self._on_agent_response)
async def _on_agent_response(self, event_type, data):
"""更新最后用户活动时间。"""
self._last_user_activity = time.time()
三、四类清理任务
3.1 死代码扫描
async def _scan_dead_code(self) -> list[DeadCodeReport]:
"""扫描未被引用的函数和类。
策略:
1. 遍历 src/ 目录下所有 .py 文件
2. 提取所有 public 函数/类的定义
3. 检查是否在测试文件或配置中被引用
4. 报告疑似死代码(未被任何文件 import 或调用)
"""
dead_codes = []
for py_file in Path("src").rglob("*.py"):
# 使用 AST 解析提取函数/类定义
tree = ast.parse(py_file.read_text())
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
if node.name.startswith("_"):
continue # 跳过私有方法
# 检查是否被引用
if not _is_referenced(node.name, py_file):
dead_codes.append(DeadCodeReport(
file=str(py_file),
name=node.name,
line=node.lineno,
type=type(node).__name__,
))
return dead_codes
3.2 文档一致性检查
async def _check_doc_consistency(self) -> list[InconsistencyReport]:
"""检查文档与代码的一致性。
策略:
1. 扫描 README.md 中的 API 接口描述
2. 对比 src/ 中的实际函数签名
3. 检查 config/ 中的配置项文档
4. 报告不一致项
"""
issues = []
# 检查 README 中的函数签名
readme = Path("README.md").read_text()
# 提取文档中提到的函数名和参数
doc_functions = _extract_documented_functions(readme)
for func_name, doc_params in doc_functions.items():
actual_params = _get_actual_signature(func_name)
if actual_params is None:
issues.append(InconsistencyReport(
type="function_not_found",
doc_reference=func_name,
message=f"文档引用的函数 {func_name} 在代码中不存在",
))
elif set(doc_params) != set(actual_params):
issues.append(InconsistencyReport(
type="parameter_mismatch",
doc_reference=func_name,
message=f"参数不一致: 文档={doc_params}, 实际={actual_params}",
))
return issues
3.3 技能花园维护
async def _tend_skill_garden(self) -> GardenReport:
"""维护技能花园。
与 Curator 的区别:
- Curator 负责技能的创建和合并(高频)
- EntropyManager 负责技能的使用率审计(低频)
策略:
1. 统计每个技能过去 30 天的使用次数
2. 标记使用率 <10% 的技能为"休眠"
3. 标记与其他技能相似度 >0.9 的技能为"待合并"
"""
garden = GardenReport()
for skill in skill_manager.list_all():
usage_count = skill.usage_count_30days
if usage_count == 0:
garden.dormant.append(skill.name)
elif usage_count < 3:
garden.low_usage.append(skill.name)
# 检查相似度
for other in skill_manager.list_all():
if other.id != skill.id:
similarity = _compute_similarity(skill, other)
if similarity > 0.9:
garden.merge_candidates.append(
(skill.name, other.name, similarity)
)
return garden
3.4 旧轨迹清理
async def _cleanup_old_traces(self) -> CleanupReport:
"""清理过期的 TaskTrace 日志。
策略:
1. 扫描 data/traces/ 目录
2. 删除 30 天前的轨迹文件
3. 压缩 7-30 天的轨迹文件(只保留摘要)
"""
report = CleanupReport()
traces_dir = Path("data/traces")
for trace_file in traces_dir.glob("*.jsonl"):
age_days = (time.time() - trace_file.stat().st_mtime) / 86400
if age_days > 30:
trace_file.unlink()
report.deleted += 1
elif age_days > 7:
# 压缩:只保留摘要字段
_compress_trace(trace_file)
report.compressed += 1
return report
四、报告与通知
4.1 熵管理报告
每轮清理完成后,EntropyManager 生成一份报告并通过 EventBus 发布:
@dataclass
class EntropyReport:
"""熵管理周期报告。"""
timestamp: str
dead_codes_found: int
doc_inconsistencies: int
dormant_skills: int
traces_cleaned: int
duration_ms: int
# 发布到 EventBus
await self._event_bus.emit("entropy_cycle_complete", report)
4.2 告警机制
当发现严重问题时(如死代码数量 >50 或文档不一致 >10),主动通知用户:
if len(dead_codes) > 50:
await self._event_bus.emit(
EventType.SYSTEM_HEALTH_WARNING,
{"type": "dead_code_accumulation", "count": len(dead_codes)}
)
五、复用现有调度模式
5.1 学习 AgentPool._cleanup_loop
WeClaw 的 AgentPool 已经有一个后台清理循环(L354-381),EntropyManager 的调度模式直接复用:
# AgentPool._cleanup_loop 的现有模式(参考)
async def _cleanup_loop(self):
"""定期清理过期的 Agent 实例。"""
while not self._stop_event.is_set():
try:
await asyncio.wait_for(
self._stop_event.wait(), timeout=self._cleanup_interval
)
break
except asyncio.TimeoutError:
pass
# 执行清理
async with self._lock:
expired = [
sid for sid, (agent, ts) in self._instances.items()
if time.time() - ts > self._session_timeout
]
for sid in expired:
await self._instances.pop(sid)[0].aclose()
EntropyManager 使用完全相同的模式,只是清理对象不同。
5.2 停止机制集成
在 gui_app.py 的关闭流程中:
# gui_app.py closeEvent / _shutdown
async def _shutdown(self):
# ... 其他清理 ...
if self._entropy_manager:
await self._entropy_manager.stop() # 优雅停止
# ...
六、性能保障
6.1 每项检查的时间上限
| 检查项 | 预估耗时 | 频率 |
|---|---|---|
| 死代码扫描 | ~300ms | 每天一次 |
| 文档一致性 | ~200ms | 每天一次 |
| 技能花园 | ~500ms | 每周一次 |
| 轨迹清理 | ~100ms | 每天一次 |
所有检查项都在 500ms 以内,且只在系统空闲时运行。
6.2 资源隔离
async def run_cycle(self):
"""执行一轮熵管理。"""
# 设置每个检查的超时
for check_name, check_fn in self.CHECKS.items():
try:
result = await asyncio.wait_for(
check_fn(), timeout=5.0 # 单项检查最多 5 秒
)
self._report(check_name, result)
except asyncio.TimeoutError:
logger.warning("熵管理检查超时: %s", check_name)
except Exception as e:
logger.error("熵管理检查失败: %s - %s", check_name, e)
七、与 Curator 的关系
EntropyManager 和 Curator 的职责划分:
| 维度 | Curator | EntropyManager |
|---|---|---|
| 触发方式 | 实时(对话后触发) | 空闲时(后台周期性) |
| 技能管理 | 创建、合并、评分 | 使用率审计、休眠标记 |
| 文档检查 | ❌ | ✅ |
| 死代码扫描 | ❌ | ✅ |
| 轨迹清理 | ❌ | ✅ |
| 频率 | 每次对话 | 每天/每周 |
| 优先级 | 高(影响用户体验) | 低(后台运行) |
两者互补而非替代:Curator 负责"增量"(不断学习新技能),EntropyManager 负责"减量"(清理过时内容)。
八、核心教训
8.1 熵管理是"可选但必要"的
单个熵增项的影响很小——一个过时的文档、一个未使用的函数——但累积起来会显著降低 Agent 的执行质量。就像内存泄漏不会立即导致崩溃,但长期运行必然出问题。
8.2 空闲触发比定时触发更好
定时触发(如每天凌晨 3 点)的问题是:如果用户在凌晨 3 点正好在用系统,清理任务会和用户请求竞争资源。空闲触发(最近 5 分钟无请求)天然避免了这个问题。
8.3 报告比自动修复更重要
V1 方案曾设想 EntropyManager 自动修复文档不一致——但代码级评审认为这太危险(自动修改文档可能引入新的不一致)。V3 方案改为只报告不修复:生成报告供开发者审查,由人类决定是否修复。
📖 相关文章
本文是 WeClaw 专栏第四季的第 4 篇(总第 65 篇)。如果这篇文章对你有帮助,欢迎给项目点个 Star ⭐