首页
/ omo/lazycodex 常量文件拆分实战:delegate-task constants.ts 重构执行计划深度解析

omo/lazycodex 常量文件拆分实战:delegate-task constants.ts 重构执行计划深度解析

2026-09-04 10:23:13作者:胡唯隽

在 omo/lazycodex(oh-my-openagent)这个由大量 TypeScript 包组成的 agent harness 中,task 委托工具的 constants.ts 曾是一个 654 行、身兼 6 种职责的"上帝常量文件"。本文基于仓库中一份真实的重构执行计划(execution-plan),完整还原"如何安全拆分一个被 10+ 处内部/外部模块重度依赖的常量文件"的全流程:从预检分析、职责盘点、导入依赖映射,到逐文件拆分、桶(barrel)重导出与零消费者变更的提交策略,并结合当前仓库源码验证该计划的实际落地形态。读完本文,你能掌握一套可迁移到任何中大型 TypeScript 项目的"零消费者破坏"模块拆分方法论。

一、计划背景:为什么要拆 constants.ts

执行计划的 Context 部分给出了触发拆分的三个硬事实:

  1. src/tools/delegate-task/constants.ts 当时为 654 行,承载 6 种不同职责,违反了项目的 200 LOC 模块代码约束规则(modular-code-enforcement);
  2. 其中被普遍引用的 CATEGORY_MODEL_REQUIREMENTS 实际并不在 constants.ts 中,而是在 src/shared/model-requirements.ts(当时 311 行,同样违反 200 LOC 规则);
  3. 因此本次重构是"双文件拆分":拆分 constants.ts 本体,同时把 CATEGORY_MODEL_REQUIREMENTSmodel-requirements.ts 中剥离。

这个文件之所以危险,是因为它集中了 task 委托工具的全部静态知识:内置分类的默认配置、分类描述、分类专属 prompt 附加段、plan 智能体的系统提示词与身份判定逻辑。任何一个拼写错误或误删导出,都会波及整个委托执行链路。

二、预检分析:职责盘点与 LOC 核算

执行计划的第一步不是动手,而是把两个文件的所有职责逐项列出并估算行数:

constants.ts 的 6 项职责

# 职责 内容 规模
1 Category prompt appends 8 个模板字符串常量 约 274 行 prompt 文本
2 DEFAULT_CATEGORIES Record<string, CategoryConfig> 约 10 行
3 CATEGORY_PROMPT_APPENDS 分类 → prompt 映射 约 10 行
4 CATEGORY_DESCRIPTIONS 分类 → 描述映射 约 10 行
5 Plan agent prompts 2 个模板字符串 + 4 个构建函数 约 250 行 prompt 文本
6 Plan agent identity utils isPlanAgentisPlanFamily 约 30 行

model-requirements.ts 的 3 项职责

  1. 类型定义(FallbackEntryModelRequirement);
  2. AGENT_MODEL_REQUIREMENTS(约 146 行);
  3. CATEGORY_MODEL_REQUIREMENTS(约 148 行)。

值得注意:prompt 文本类代码虽然行数巨大,但在 modular-code-enforcement 规则下豁免 200 LOC 限制——这一点在计划中针对 2c(约 280 行)与 2d(约 270 行)两个新文件被明确标注。也就是说,拆分目标不是机械地把行数砍到 200 以下,而是按职责边界切分,prompt 文本天然聚类的文件允许超限。

三、导入依赖映射:拆分安全性的前提

计划的核心洞察是:只要保留桶文件(barrel)重导出,所有消费者就一行都不用改。但前提是先把依赖面摸清楚。计划列出了三类消费者:

内部消费者(delegate-task/ 目录内)

文件 导入符号
categories.ts DEFAULT_CATEGORIESCATEGORY_PROMPT_APPENDS
tools.ts CATEGORY_DESCRIPTIONS
tools.test.ts DEFAULT_CATEGORIESCATEGORY_PROMPT_APPENDSCATEGORY_DESCRIPTIONSisPlanAgentPLAN_AGENT_NAMESisPlanFamilyPLAN_FAMILY_NAMES
prompt-builder.ts buildPlanAgentSystemPrependisPlanAgent
subagent-resolver.ts isPlanFamily
sync-continuation.ts isPlanFamily
sync-prompt-sender.ts isPlanFamily
index.ts export * from "./constants"(桶文件)

外部消费者(import from "../../tools/delegate-task/constants"

文件 导入符号
agents/atlas/prompt-section-builder.ts CATEGORY_DESCRIPTIONS
agents/builtin-agents.ts CATEGORY_DESCRIPTIONS
plugin/available-categories.ts CATEGORY_DESCRIPTIONS
plugin-handlers/category-config-resolver.ts DEFAULT_CATEGORIES
shared/merge-categories.ts DEFAULT_CATEGORIES
shared/merge-categories.test.ts DEFAULT_CATEGORIES

CATEGORY_MODEL_REQUIREMENTS 的消费者

文件 导入路径
tools/delegate-task/categories.ts ../../shared/model-requirements

这些依赖在当前仓库中依然可以逐一验证,说明该计划的预检映射是准确的:

  • prompt-builder.tsimport { buildPlanAgentSystemPrepend, isPlanAgent } from "./constants",并在 prompt 组装时以 isPlanAgent(agentName) 为开关注入 plan 系统提示词;
  • sync-continuation.tssync-prompt-sender.ts 都从 ./constants 引入 isPlanFamily,分别用于"恢复会话时是否允许 task 工具"(const allowTask = isPlanFamily(resumeAgent))和"同步 prompt 路由时是否放行 task";
  • 外部消费者 builtin-agents.tsavailable-categories.tsatlas/prompt-section-builder.ts 至今仍从 ../tools/delegate-task/constants 导入 CATEGORY_DESCRIPTIONScategory-config-resolver.ts 导入 DEFAULT_CATEGORIES 并实现"用户自定义分类优先、内置分类兜底"(userCategories?.[categoryName] ?? DEFAULT_CATEGORIES[categoryName])。

依赖图还揭示了一个跨层语义isPlanFamily 不只是提示词注入开关,它同时控制"互斥委托阻断"与"task 工具权限"——plan 家族(plan + prometheus)是编排者,可以下发 task 调用,这正是后续 constants.tsPLAN_FAMILY_NAMES = ["plan", "prometheus"]COORDINATOR_AGENT_NAMES 守卫存在的根源。

四、分步执行:从建分支到提 PR

Step 1:创建分支

git checkout -b refactor/split-category-constants dev

分支命名直接体现重构意图与目标(refactor/split-*),基线为 dev

Step 2:把 constants.ts 拆分为 5 个职责单一的文件

2a. default-categories.ts

  • 移入 DEFAULT_CATEGORIES record;
  • 从 config schema 导入 CategoryConfig 类型;
  • 约 15 行。

2b. category-descriptions.ts

  • 移入 CATEGORY_DESCRIPTIONS record;
  • 无依赖;
  • 约 12 行。

2c. category-prompt-appends.ts

  • 移入全部 8 个 *_CATEGORY_PROMPT_APPEND 模板字符串常量;
  • 移入 CATEGORY_PROMPT_APPENDS 映射 record;
  • 无依赖(全部是自带上下文、自包含的模板字符串);
  • 约 280 行(以 prompt 文本为主,豁免 200 LOC 限制)。

2d. plan-agent-prompt.ts

  • 移入 PLAN_AGENT_SYSTEM_PREPEND_STATIC_BEFORE_SKILLS
  • 移入 PLAN_AGENT_SYSTEM_PREPEND_STATIC_AFTER_SKILLS
  • 移入 renderPlanAgentCategoryRows()renderPlanAgentSkillRows()
  • 移入 buildPlanAgentSkillsSection()buildPlanAgentSystemPrepend()
  • 依赖:来自 agents 的 AvailableCategoryAvailableSkill 类型,来自 shared 的 truncateDescription
  • 约 270 行(以 prompt 文本为主,豁免)。

2e. plan-agent-identity.ts

  • 移入 PLAN_AGENT_NAMESisPlanAgent()
  • 移入 PLAN_FAMILY_NAMESisPlanFamily()
  • 无依赖;
  • 约 35 行。

拆分顺序暗含依赖考量:2a–2c、2e 完全无依赖、可独立验证;2d 是唯一带跨模块类型依赖的文件,因此放在最后,且计划中显式标注了它需要导入的类型来源。

Step 3:把 constants.ts 改写为桶重导出文件

原文件全部内容替换为对 5 个新文件的 re-export。这一步是整个计划的枢纽:它让所有既有导入者保持 100% 向后兼容——内部消费者、外部消费者、桶文件 index.tsexport * 全部照常工作。

Step 4:拆分 model-requirements.ts

4a. 新建 src/shared/category-model-requirements.ts

  • 移入 CATEGORY_MODEL_REQUIREMENTS record;
  • ./model-requirements 导入 ModelRequirement 类型;
  • 约 150 行。

4b. 更新 src/shared/model-requirements.ts

  • 删除 CATEGORY_MODEL_REQUIREMENTS
  • 增加重导出:export { CATEGORY_MODEL_REQUIREMENTS } from "./category-model-requirements"
  • 保留类型(FallbackEntryModelRequirement)与 AGENT_MODEL_REQUIREMENTS
  • 收敛到约 165 行(低于 200 行红线)。

Step 5:验证导入无破坏

  • bun run typecheck —— 确认所有 import 可解析;
  • bun test —— 确认无行为回归;
  • bun run build —— 确认构建成功。

Step 6:LSP 诊断检查

对所有新建与修改文件检查 lsp_diagnostics 是否为空。这一条把验证从"编译器/测试通过"进一步收紧到"无诊断告警",防止死代码、未使用导出等隐性问题进入主干。

Step 7:提交并创建 PR

  • 单个原子提交:refactor: split delegate-task constants and category model requirements into focused modules
  • 附 PR 描述。

原子提交保证 code review 时 diff 只有"移动 + 重导出",没有任何行为混入,reviewer 可以确信这是一次纯结构性重构。

五、文件变更清单与"零消费者变更"原则

文件 动作
src/tools/delegate-task/constants.ts 改写为桶重导出
src/tools/delegate-task/default-categories.ts 新增
src/tools/delegate-task/category-descriptions.ts 新增
src/tools/delegate-task/category-prompt-appends.ts 新增
src/tools/delegate-task/plan-agent-prompt.ts 新增
src/tools/delegate-task/plan-agent-identity.ts 新增
src/shared/model-requirements.ts 删除 CATEGORY_MODEL_REQUIREMENTS,增加重导出
src/shared/category-model-requirements.ts 新增

对任何消费者文件零修改。 全部既有导入经由桶重导出继续生效。这是本文最值得内化的一条原则:重构的安全边界不靠小心修改消费者来维持,而靠重导出契约来维持——消费者越多,这一原则的价值越大。

六、当前仓库验证:计划的实际落地形态

从源码结构看,该执行计划在 omo/lazycodex 仓库中已被(部分)落地,且实际演化与计划同构但略有差异,这本身就是"计划驱动重构 + 后续持续演化"的真实样本:

  1. 分类 record 已迁出 constants.ts。当前 constants.ts 只有 414 行(从 654 行收敛),其开头就是标准的桶重导出:

    export {
      BUILTIN_CATEGORY_REQUIRES_MODEL,
      CATEGORY_DESCRIPTIONS,
      CATEGORY_PROMPT_APPENDS,
      CATEGORY_PROMPT_APPEND_RESOLVERS,
      DEFAULT_CATEGORIES,
    } from "./builtin-categories"
    

    DEFAULT_CATEGORIESCATEGORY_PROMPT_APPENDSCATEGORY_DESCRIPTIONS 等 record 已迁入 builtin-categories.ts。值得注意的是,实际实现没有把三份 record 机械拆成三个文件,而是收敛为一份 BUILTIN_CATEGORY_DEFINITION[](按 Google/OpenAI/Anthropic/Kimi 分组),再通过统一的 buildCategoryRecord() 工厂派生出各 record——从源码结构看,这是比计划更进一步的"单一数据源"设计,消除了计划中三份 record 各自维护的潜在漂移风险。

  2. constants.ts 保留了计划 2d/2e 对应的职责PLAN_AGENT_SYSTEM_PREPEND_STATIC_BEFORE_SKILLS / AFTER_SKILLS 两大模板字符串、renderPlanAgentCategoryRowsrenderPlanAgentSkillRowsbuildPlanAgentSkillsSectionbuildPlanAgentSystemPrepend,以及 PLAN_AGENT_NAMESisPlanAgentPLAN_FAMILY_NAMESisPlanFamily 等身份工具仍留在该文件中。plan agent 的系统提示词本身就是"先派发 explore/librarian 智能体收集上下文、再输出依赖图 + 并行执行波次 + 分类/技能推荐"的强制协议——这也解释了为什么这类 prompt 文本天然豁免 200 LOC 限制。

  3. model-requirements 拆分已完成并进一步下沉到共享包。当前 shared/model-requirements.ts 已是一个仅 5 行的重导出垫片(shim),把 FallbackEntryModelRequirement 类型与 AGENT_MODEL_REQUIREMENTSCATEGORY_MODEL_REQUIREMENTS 全部转发到 @oh-my-opencode/model-core。而 packages/model-core/src/category-model-requirements.ts(131 行)正是计划 Step 4a 所描述的新家:CATEGORY_MODEL_REQUIREMENTS record 按分类提供 fallbackChain,例如 visual-engineering 分类的降级链为 claude-opus-5(max)kimi-k3(max)glm-5.2(max)gpt-5.6-sol(medium),每项都带 providers 白名单与推理档位(variant)。类型定义则落在 model-requirement-types.ts。消费端 categories.ts 仍然按计划中的路径 ../../shared/model-requirements 导入 CATEGORY_MODEL_REQUIREMENTS 并在分类解析时查表(const categoryReq = CATEGORY_MODEL_REQUIREMENTS[categoryName])——消费者零变更原则在真实代码中得到兑现

  4. 委托链文档与计划相互印证delegate-task 目录的 AGENTS.md 描述了该目录的双执行模式(background/sync)、同步执行链(sync-task.ts → sync-session-creator.ts → sync-prompt-sender.ts → sync-session-poller.ts → sync-result-fetcher.ts)与分类解析流程(用户自定义分类优先,回退到内置分类)。计划中依赖映射涉及的 sync-prompt-sender.tssync-continuation.tscategories.ts 都是这条链上的一环,说明拆分所触及的正是委托执行链的核心静态层。

七、方法论提炼:如何安全拆分一个"上帝常量文件"

把这份执行计划抽象成可复用流程,共五步:

  1. 先盘点职责,再动手:为源文件逐条列出职责与行数,明确哪些内容豁免行数规则(如 prompt 文本),避免按行数机械切分导致语义割裂;
  2. 画完整导入依赖图:区分内部消费者、外部消费者、桶文件三类,按符号粒度记录(哪个文件 import 了哪些导出)。这一步决定了桶文件必须保留哪些重导出;
  3. 按职责建文件,无依赖者优先:无外部依赖的 record/常量先拆(可独立验证),带跨模块类型依赖的模块最后拆,并在计划中显式列出依赖来源;
  4. 桶重导出兜底:源文件改写为 re-export 层,实现"消费者零变更";被拆分出的大块(如 CATEGORY_MODEL_REQUIREMENTS)同样在新家加类型导入、在旧位置加重导出;
  5. 三层验证 + LSP 诊断typecheck(导入可解析)→ test(行为无回归)→ build(构建成功)→ lsp_diagnostics(无隐性问题),全部通过后再以单个原子提交 + PR 描述收尾。

这套流程在 omo/lazycodex 的实际演化中经受住了检验:即使后续架构调整(内置分类改为按 provider 分组的定义数组、模型需求下沉到 model-core 共享包)偏离了原计划的文件名与文件数,重导出契约始终保护着所有消费者——这正是"以文档为计划、以源码为契约"的重构工程实践。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384