M20 自举第二期(D3 自举闭环)实施计划

jkos repo:https://github.com/skywalk163/jkos-ai-platform
Jikuai AI OS —— 基于 FreeBSD + deepseek-harness 构建的企业级 AI 服务操作系统

Context

M19 已闭环(基础设施迁移 PostgreSQL + NATS),M20 是路线图的第 6–7 周里程碑,目标一句话:JKOS 管理 JKOS —— 用 M17 建成的自动化优化引擎驱动 JKOS 自身的「探索 / 复盘 / 优化」流程,并与 deepseek-harness 融合成自举闭环。

当前真实缺口(本计划的立足点):jkos_core/optimization/ 的 OptimizationEngine 门面虽有「缓存 / 模板 / 探索」三层路径,但它的探索层是模拟实现(automation_engine.py:139 execute_with_exploration,按 EXPLORE_BASE=1500 × EXPLORE_SUBTASKS=10 假算出 15000 token),与真实引擎 jkos_core/exploration/(分解→检索→搜索→实验→沉淀)之间没有任何胶水代码;ProcessSolidifier.solidify() 与 TemplateManager.create_template() 虽已具备,也没有被串进任何闭环。所以「自举」目前只是文档里的口号,代码里不成立。

M20 要做的是把这条链路真正打通,并用可量化证据(首次全量 vs 复现的 Token / 耗时)证明闭环成立。

D3 的业务定义见 docs/DSH-AI中台开发计划-三案例驱动.md:115:单元测试生成 ——「选定函数 → 用例设计 → 生成 → 运行 → 覆盖率报告」,管道式。

已确认的三个决策(用户拍板,实施中不得偏离)

  1. 20.1 采用混合模式:有 LLM 走真实生成,无则确定性兜底,必须离线可测、CI 可复现。
  2. 20.2 采用 MCP 工具形态:新增工具经 M16 ToolBridge 暴露给 harness agent,验收沿用 M16 AC-3 口径。
  3. 20.4 范围为 README + 活跃文档 + 代码层:scripts/ 下 40+ 历史部署脚本与历史计划文档 docs/DSH-AI中台开发计划-三案例驱动.md 保留原文(与 M15「历史文档称谓不变」口径一致)。

一、模块落点

新增包 jkos_core/selfboot/(自举编排层):

文件职责
__init__.py门面导出 SelfBootstrapEngine / LoopResult / D3Executor / FunctionTarget
d3_testgen.pyD3 执行器:定位函数、设计用例、生成代码、运行、覆盖率;含 PytestRunner 协议与 SubprocessPytestRunner 真实实现
loop.py四段闭环编排 SelfBootstrapEngine + PipelineSearcher

为什么不放进 optimization/:optimization/ 是 M12 的组件层(各模块已 ≥85% 覆盖,改动即回归风险);自举是编排层,同时消费 exploration 与 optimization 两个包。
为什么不叫 bootstrap/:避免与 jkos_core/bootstrap.py(build_components / AppComponents 组件装配)语义混淆。

核心逻辑放模块、scripts/ 只放薄 CLI:既满足「20.1 D3 自举脚本」的交付形态,又能被 pytest 直接导入覆盖。先例是 scripts/demo_three_cases.py(逻辑留在 jkos_core)。

默认数据目录 data/selfboot/(.gitignore:9 已忽略 data/)、报告目录 reports/m20/(.gitignore:13 已忽略 reports/),二者均支持环境变量覆盖,避免污染仓库。

二、20.1 D3 执行器(d3_testgen.py)

函数定位 locate_function(module_path, qualname) -> FunctionTarget:用 ast.parse 遍历 FunctionDef / AsyncFunctionDef(支持 Cls.method 与嵌套函数),ast.get_source_segment 取源码,返回 lineno / end_lineno / 参数签名 / 是否有 docstring。不 import 被测模块(避免导入副作用),找不到抛 FunctionNotFound。

五阶段流水线(每阶段独立方法,产出 evidence dict):select → design → generate → run → coverage。

生成路径(混合模式的核心):

  • 真实路径:await llm.chat_text(prompt, purpose="selfboot", ...),prompt 要求只输出 JSON 数组(每条含 name / code / rationale)。解析沿用本项目既定范式 —— 参考 tenants/dev/analyzers/llm_review.py 的 parse_llm_findings():剥 ``` 围栏 → 整段 json.loads → 正则抽首个 [...] → 全失败退化。
  • 降级判定基于 result.simulated,不是判断 llm is None —— 因为 comps.llm 是 LLMRouter(bootstrap.py:47),永不为 None,无 API Key 时链路末尾的 SimulatedProvider 会返回 simulated=True 的 [模拟输出] 文本。
  • 确定性兜底:按签名参数构造安全入参(有默认值取默认值,int 取 0/1/-1 边界,str 取 ""/"x"),生成两类断言 ——「可调用且不抛异常」+「同输入两次结果一致」。对任意纯函数安全,不会因语义猜错而假失败。

生成代码的三层安全防护(生成物是不可信输入):

  1. compile(src, path, "exec") 语法校验;
  2. AST 白名单:仅允许 import pytest / 被测模块、FunctionDef、Assign、Assert、Call;出现 subprocess / os / eval / exec / open / __import__ 一律拒绝;
  3. 任一层失败即回退确定性模板,并记录 generation_source: "llm" | "fallback"。

生成物只写 tempfile.mkdtemp(),绝不落仓库。

运行步骤的可注入设计(关键):

class PytestRunner(Protocol):
    async def run(self, test_dir: Path, target_module: str) -> RunOutcome: ...

SubprocessPytestRunner 是真实实现:sys.executable -m pytest <tmp> -p no:cacheprovider -q --no-header,cwd=tmpdir、timeout=60、显式剔除 PYTEST_ADDOPTS(防止继承仓库 --cov 造成递归)。单元测试注入 FakePytestRunner,绝不真跑子进程。

覆盖率:优先真实值 —— 子进程用 coverage run --data-file=<tmp> -m pytest 再 coverage json,只解析目标模块的 percent_covered;coverage 不可用时退化为代理指标(由用例数/断言数推导)并标注 coverage_source: "proxy",保证离线可测。

与探索引擎的接法(不改 exploration/optimization 任何既有代码):Experimenter.experiment(hypothesis, *, executor, dataset_size)(experimenter.py:43-51)的 executor 契约是 (hypothesis, sample_size) -> (success, metric, details),支持 async/sync。D3 执行器实现 __call__ 即作为 executor 传入 ExplorationEngine.explore(task, executor=d3, dataset_size=N)。

三、20.1 闭环编排(loop.py)

段调用产物
1 探索ExplorationEngine.explore(task, executor=d3, dataset_size=N)ExploreResult
2 固化ProcessSolidifier.solidify(result) + record_run(...)Process(5 步 + verify)
3 模板化TemplateManager.create_template(process) + has_template(task) 自检Template
4 自动化执行recommend_template(task) → execute_template(tpl)AutomationResult(source="template")

模板命中可行性已核实(演示是否成立的关键):create_template 令 template.task = process.task = exploration.task;recommend_template 按 keyword_score = |q∩t| / |q| 打分,阈值 MIN_TEMPLATE_MATCH = 0.5(template_manager.py:35)。第二次必须使用逐字符相同的 task 字符串(覆盖率 1.0 → 必然命中);一旦拼接「(复现)」之类后缀会拉大分母、可能跌破阈值 —— 演示与测试均禁止拼接后缀。

两个必须避开的坑:

  1. 第 4 段不能走 OptimizationEngine.execute() —— 它第一层的 Token 缓存按精确 task 命中会抢跑,返回 source="cache" 而非 "template"。第 4 段必须直连 TemplateManager(template-first)。
  2. explore() 在命中知识库时会早退并跳过实验(exploration/engine.py:81-93),因此编排必须注入独立的空 KB。

让固化步骤等于 D3 五阶段:默认 SolutionSearcher 会扫全仓 doc header 当步骤(产出垃圾流程)。编排注入 PipelineSearcher(duck-type:async def search(task, *, limit=8)),返回单个候选,description 为五阶段文案,使 _extract_steps() 走 solution.steps 分支。

结果对象 LoopResult:task / target / stages(4 段:name、ok、source、token_used、duration_ms、detail)/ process_id / template_id / template_hit / first_run / replay_run / savings / llm(simulated、provider)。

量化口径复用既有函数:首次全量取 estimate_full(task)(token_optimizer)与真实 LLM total_tokens;复现成本取 execute_template 的 result.token_used(按 STEP_COST=20 + len(action) 逐步骤累加,template_manager.py:423)。

四、20.2 harness 融合(MCP 工具)

改动集中在 jkos_core/mcp/server.py 三处 + 一处分类分支:

  1. PREDEFINED_TOOLS(L103-232)新增两条:dsh_optimize_execute(参数 task 必填、target 可选、require_approval 可选)与 dsh_optimize_stats(参数 limit 可选)。
  2. ToolExecutor.execute() 的 if/elif 链(L244-276)加两个分支 + 新增 _execute_optimize_execute / _execute_optimize_stats 方法。
  3. 惰性单例:仿 jkos_core/harness/gateway.py:214-222 的模块级 getter,新增 _get_selfboot(comps)(None 时构造 + await initialize())与 _close_selfboot();comps 取自 getattr(self._engine, "comps", None)(workflow/engine.py:70 已保存),不新增构造参数,create_app / cli.py 零改动;comps=None 时 llm 缺失,D3 自动走确定性降级,工具仍可用。
  4. ToolExecutor.cleanup()(L541-543)追加 await _close_selfboot() —— MCPServer.cleanup()(L938-941)已调用它,链路已通,单例持有的 sqlite 连接与临时目录得以释放。

分类:在 _default_tool_category()(L38-54)加一支 dsh_optimize → ToolCategory.GOVERNANCE。严禁新增 ToolCategory 枚举值 —— registry.get_categories()(registry.py:472-474)返回枚举全集,tests/test_api_plugin_tool_routes.py:794 断言 len(categories) == 9,新增枚举值会打破它。

可见性:沿用 _sync_builtin_tools() 的 ToolVisibility.CORE,不写入 _tool_owners → 匿名可见,符合 M16 安全跟进口径,也满足 AC-3 的带 token 调用。

响应裁剪:工具只返回 LoopResult 摘要(task / template_hit / source / savings / llm),不返回生成代码正文,避免撑爆 MCP 响应。

已排查的兼容性风险:全仓测试没有任何写死工具数 11 的断言,全部用 len(PREDEFINED_TOOLS) 动态推导(test_mcp.py:59、test_mcp_server.py:434/474/548、test_mcp_auth.py:69/139、test_m14_mcp_mt.py:88/93/98/115/116/192),11→13 不会失败;scripts/cov82_harness_ac3.sh:65 也只打印工具数。

五、20.3 闭环演示与验收

  • scripts/d3_selfboot.py:CLI(--target / --out reports/m20/d3_selfboot_loop.json / --no-llm),成功打印 M20.3-DEMO-OK,失败 M20.3-DEMO-FAIL。
  • scripts/m20_accept82.sh:仿 scripts/m18_accept82.sh 套路(cd /data/dsh/code-jkos || exit 1、PY312 / PY311 双 venv、pytest -p no:cacheprovider -q --no-header 2>&1 | tail -2),五段:py3.12 全量 → py3.11 全量 → tests/test_selfboot/ → D3 闭环演示 + --no-llm 复跑(证明确定性降级)→ harness AC-3 口径复验(含新工具),终态 M20-ACCEPT-OK。
  • docs/M20-自举闭环复盘报告.md:三决策、四段证据、首次 vs 复现量化表、LLM / 降级双跑对比、已知问题。
  • docs/AI中台开发计划-M17plus.md(L154-171):任务表加 ✅、新增「### M20 验收」勾选块、产出物补实际路径。
  • CHANGELOG.md 顶部新增 M20 条目(新增 / 变更 / 测试 / 验收闭环四节,仿 M19)。

六、20.4 旧称与陈旧信息收口

已核实的待改点:

  • README.md:7 badge tests-789 passed → 实测值;:38 「8 个预定义工具」→ 13;:219 「789 passed」→ 实测值;:221-232 工具表缺 dsh_approval_* 三条 + 新增两条 optimize;:241 / :244 ruff check dsh_core/、mypy dsh_core/ 包名残留 → jkos_core/。
  • jkos_core/mcp/server.py:106、:119 「DSH 中台」→ JKOS;:360、:372 「DeepSeek 中台」→ JKOS。
  • README.md:9、CONTEXT.md、CHANGELOG.md 里的「原名 DSH AI 中台」属品牌沿革说明,保留。
  • scripts/** 与 docs/DSH-AI中台开发计划-三案例驱动.md(历史计划文档)不动。

七、测试方案

新增 tests/test_selfboot/(仿 tests/test_optimization/ 的 tmp_path 注入模式):

  • conftest.py:sample_module(tmp_path 写被测模块)、d3_executor、selfboot(tmp db 的 OptimizationEngine + ExplorationEngine(searcher=PipelineSearcher, knowledge_base=tmp) + FakePytestRunner)。
  • test_d3_locate.py(~6):同步/异步/类方法/嵌套函数定位、FunctionNotFound、行号与源码正确。
  • test_d3_pipeline.py(~10):五阶段顺序;LLM JSON 解析成功;simulated=True → fallback;垃圾输出容错;语法非法回退;AST 黑名单拦截;全过 / 有失败两态;覆盖率达标两态;coverage_source 标注。
  • test_d3_runner.py(~5):命令构造(-p no:cacheprovider、PYTEST_ADDOPTS 已剔除、timeout);超时分支;非零退出解析;coverage json 解析;coverage 缺失 → proxy。全部用 monkeypatch 打桩,不真跑子进程。
  • test_selfboot_loop.py(~8):四段顺序;LoopResult 字段完整;同 task 命中刚创建的模板;paraphrase 不命中(锁住阈值语义);token_saved_ratio > 0.9;报告落盘;replay 幂等;close 释放资源。
  • test_selfboot_mcp.py(~6):两条工具在 PREDEFINED_TOOLS;execute() 成功;/tools 与 /mcp tools/list 均含且 inputSchema 驼峰合规;匿名可见;cleanup 关闭单例;reset_selfboot 幂等。

覆盖率:M19 基线 93%(pyproject.toml:53 只配 --cov,无 --cov-fail-under 硬门槛,靠比对)。新增模块每条降级 / 异常分支都要有用例,确保 TOTAL ≥ 93%;不可达的防御分支才用 # pragma: no cover 并注明理由。

八、提交切分(依赖 1→2→3→4→5)

  1. feat(selfboot): D3 执行器 —— d3_testgen.py + 前三个测试文件
  2. feat(selfboot): 四段闭环编排 —— loop.py + loop 测试
  3. feat(mcp): dsh_optimize_execute / dsh_optimize_stats —— server.py + mcp 测试
  4. feat(scripts): D3 自举演示脚本与 0.82 验收脚本
  5. docs(m20): 复盘报告 + 计划回填 + CHANGELOG + 旧称收口

每笔提交跑 tests/test_selfboot + tests/test_optimization;第 3 笔后加跑 MCP 五件套;第 5 笔前跑全量。

九、验证方式

本地(Windows py3.14)

python -m pytest tests/test_selfboot tests/test_optimization -q
python -m pytest tests/test_mcp.py tests/test_mcp_server.py tests/test_mcp_auth.py \
  tests/test_m14_mcp_mt.py tests/test_api_plugin_tool_routes.py -q
python -m pytest -q                     # 全量:目标 ≥ 1032 + 新增 passed / 3 skipped,覆盖率 ≥ 93%
python scripts/d3_selfboot.py --no-llm  # 离线确定性闭环,应打印 M20.3-DEMO-OK

0.82 服务器(FreeBSD,双 venv)

Get-Content -Raw dsh-ai-platform/scripts/m20_accept82.sh | python dsh-ai-platform/scripts/ssh82.py -
# 期望终态标记 M20-ACCEPT-OK

闭环成立的最小判据:LoopResult.template_hit is True、replay_run.source == "template"、savings.token_saved_ratio > 0.9,且四段 stages 全部 ok。

十、风险与规避

风险规避
污染仓库内真实 sqlite 库全部组件支持注入 db_path;默认落 data/selfboot/(已 gitignore);测试一律 tmp_path
知识库命中导致探索早退编排注入独立空 KB
pytest 子进程递归 / 继承仓库 --covmkdtemp() 仓库外 + cwd=tmpdir + 剔除 PYTEST_ADDOPTS;单测不真跑
模板匹配阈值 0.5 导致复现不命中第二次用逐字符相同 task;单测锁住 paraphrase 不命中的语义
Token 缓存抢跑,source 变成 cache第 4 段与 MCP 工具都走 template-first,绕开 OptimizationEngine.execute()
test_api_plugin_tool_routes.py:794 断言 categories == 9复用既有 ToolCategory,不新增枚举值
异步单例资源泄漏(4 个 sqlite 连接 + 临时目录)ToolExecutor.cleanup() 中 close 并置 None;测试用 reset_selfboot() 隔离
LLM 输出抖动导致演示失败语法 → AST → 运行三层回退,保证演示不因模型输出而崩
真实 SolutionSearcher 慢且产出垃圾步骤闭环注入 PipelineSearcher;真实搜索仅作 --real-search 对照
覆盖率因新增 ~450 行而下降降级/异常分支全覆盖;提交 5 前比对全量覆盖率
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐