Skip to content

v0.57.1 — 启动耗时 50s→8s、修复被掩盖的循环导入

Choose a tag to compare

@lza6 lza6 released this 17 Oct 17:53
· 155 commits to main since this release

概述

v0.57.0 之后继续追可用性,修了两个问题:CLI 启动要等几十秒,以及一个被导入顺序掩盖的循环导入。

启动耗时

litellm 单次导入实测约 34 秒(连带 openai / httpx / pydantic 的大量子模块),
它原本在 litellm_singleton 模块顶层导入。导入链:

src.main → cli_handlers → workflow.cli_handler → workflow
  → experience_bank → core.experience_bank → enterprise_ltm
  → litellm_singleton → litellm

结果是 clawd --version 也要等几十秒。四处改为惰性(PEP 562 保持
from ... import 兼容):

文件 改法
src/llm/litellm_singleton/__init__.py 顶层不 import litellm
src/__init__.py 公开符号按需导入,__version__ 仍即时
src/agent/__init__.py 整个 Agent 栈按需导入
src/main.py cli_handlers / cost_estimator 移入使用点

实测:import src 6.8s → 1.6s;import src.main 50.9s → ~8s。

import src.cli.entrypoint 仍有 ~20s:剩余成本来自 src.core.__init__
与 src.llm.__init__ 的急切再导出,需把约 60 个再导出惰性化,属独立工程。

被掩盖的循环导入(P0)

惰性化后暴露:

src.utils → diff_utils → src.core.__init__ → core.telemetry
  → tracing_utils → src.rag.__init__ → rag.text_indexer
  → from ..utils import get_logger        ← utils 尚未完成初始化

受影响的是以 from src.utils import ... 作为进程第一个导入的任何调用方,
包括 CodeGraph.build 的 ProcessPoolExecutor 子进程 —— 实测抛
BrokenProcessPool。

修复两步:

  1. src/utils/__init__.py 在 from . import ... 之前只提前绑定 get_logger。
    不能把整行 logger 导入提前:随后导入 .debug 子模块会把 utils.debug
    覆盖成模块对象,调用时报 TypeError: 'module' object is not callable
    (第一版修复踩了这个坑,全量测试 56 红)。
  2. core/args_parser.py / evolution.py / memory_guard.py 改为直接从
    ..utils.logger 导入。

静态可达性

惰性导出会让 scripts/reachability.py(AST 建图)看不见这些模块,
生产可达数从 429 掉到 427。在两个 __init__ 中加 TYPE_CHECKING 块显式列出
来源模块:运行时零成本,静态 AST 仍可见。实测恢复 429。

确定性测试

tests/performance/test_rag_pressure.py 原断言
inc_duration < scan_duration * 0.2 依赖墙钟,在满载机器上随机失败
(全量跑红、单独跑绿,已复现)。改为统计 SymbolExtractor.extract
调用次数,断言增量扫描重解析数为 0 —— 确定性且更贴近该优化的真实语义。

验证

ruff --select F821,F811        → All checks passed
scripts/import_sweep.py        → 0 缺陷(639 模块)
scripts/reachability.py        → 429(恢复)
pytest tests/ -q               → 3042 passed / 0 failed
pytest 受影响子集               → 892 passed
from src.utils import get_logger(首导入)→ OK
CodeGraph.build                → OK(子进程不再 BrokenProcessPool)

🤖 Generated with Claude Code