首页
/ omo-opencode 的 delegate-task 常量模块拆分实战:从 654 行 constants.ts 到单一职责模块与桶式再导出

omo-opencode 的 delegate-task 常量模块拆分实战:从 654 行 constants.ts 到单一职责模块与桶式再导出

2026-09-04 22:11:54作者:段琳惟

本文以仓库中一份真实的 PR 描述文档(pr-description.md)为主体,完整还原 omo/lazycodex(oh-my-openagent)项目中一次典型的大型常量文件重构:如何把 654 行的 delegate-task/constants.ts 拆分为 4 个单一职责模块,同时通过 re-export barrel 保证 13 处消费方的导入路径零改动,并给出可复制的验证流程。读完后你可以掌握"大文件拆分 + 桶文件兼容"这套在复杂代码库中安全重构的完整方法论,并理解 task 委派工具的类别(category)与 plan agent 提示词在源码中的真实落地位置。

一、重构背景:654 行 constants.ts 违反了什么规则

omo-opencode 的 task 工具位于 delegate-task 目录,负责把工作委派给子代理(subagent),解析类别、模型与技能(skill),并管理后台/同步两种执行流。拆分前的 constants.ts 有 654 行,捆绑了 4 个互不相关的职责:

职责 内容
类别模型配置 DEFAULT_CATEGORIESCATEGORY_DESCRIPTIONS 等内置类别记录
类别提示词文本 8 个 *_PROMPT_APPEND 常量及 CATEGORY_PROMPT_APPENDS 记录
plan agent 提示词 plan 系统提示词常量与 buildPlanAgentSystemPrepend()
plan agent 名称工具 PLAN_AGENT_NAMESisPlanAgentPLAN_FAMILY_NAMESisPlanFamily

PR 文档指出的核心问题是:654 行违反了项目 200 LOC 软限制(modular-code-enforcement.md 规则)。这条规则在仓库根目录的 AGENTS.md 中有明确记载——模块结构要求 "index.ts barrel exports, no catch-all files (utils.ts, helpers.ts, service.ts banned), 200 LOC soft limit per file"。也就是说,"禁止大杂烩文件 + 每文件 200 行软上限"是项目级的代码组织约束,本次拆分正是对该约束的一次落地。

二、拆分方案:4 个单一职责模块 + 4 行 barrel

PR 的变更清单(Changes)如下,完整继承了原始 PR 描述:

新文件 职责 LOC
default-categories.ts DEFAULT_CATEGORIESCATEGORY_DESCRIPTIONS ~25
category-prompt-appends.ts 8 个 *_PROMPT_APPEND 常量 + CATEGORY_PROMPT_APPENDS 记录 ~300(prompt-exempt)
plan-agent-prompt.ts Plan 系统提示词常量 + buildPlanAgentSystemPrepend() ~250(prompt-exempt)
plan-agent-names.ts PLAN_AGENT_NAMESisPlanAgentPLAN_FAMILY_NAMESisPlanFamily ~30
constants.ts(更新后) 4 行 re-export barrel 4

两个值得注意的细节:

  1. prompt-exempt 标记。两个提示词文件虽然各有约 300 行和约 250 行,超过了 200 LOC 软限制,但被标注为 "prompt-exempt"(提示词内容豁免行数限制)。从源码结构看,这是合理的:提示词是整体语义单元,强行按行数切断字符串模板反而损害可读性与可维护性。行数规则针对的是逻辑复杂度堆积,而不是不可分割的文本资产。
  2. 拆分的粒度标准是"单一职责"而非"等分行数"。类别配置(~25 行)、提示词追加(~300 行)、plan 提示词(~250 行)、名称判定(~30 行)各自独立成文件,每个文件只回答一个问题。

三、向后兼容:re-export barrel 与导入链

PR 文档的 Backward Compatibility 一节给出了兼容策略的全部要点:

全部 13 处消费方(13 consumers)继续从 "./constants""../tools/delegate-task/constants" 导入,零改动。再导出链为:新模块 -> constants.ts -> index.ts -> 外部消费方。

这条两级再导出链在当前仓库中依然成立,可以直接从源码验证:

  1. constants.ts 顶部将类别记录类符号整体 re-export 出去(export { ... } from "./builtin-categories"),同时保留自身的 plan agent 提示词导出;
  2. index.ts 仅 4 行,其中 export * from "./constants" 把整个常量面暴露给包外。

外部消费方的例子:builtin-agents.ts 第 22 行 import { CATEGORY_DESCRIPTIONS } from "../tools/delegate-task/constants"——无论 CATEGORY_DESCRIPTIONS 实际定义在哪个子模块里,这条导入路径始终有效。这正是 barrel 模式的价值:拆分是文件组织层面的动作,对导入方的 API 契约零影响,也因此才能做到 PR 摘要里所说的"Zero import changes across the codebase (6 external + 7 internal consumers verified)"。

CATEGORY_MODEL_REQUIREMENTS 的归属澄清

PR 文档专门有一节 Note 澄清了一个常见误区:

CATEGORY_MODEL_REQUIREMENTS 已经位于 src/shared/model-requirements.ts,无需移动;AGENTS.md 中关于它在 constants.ts 的说法已过时。

这类"文档与现实漂移"在多模块重构中非常典型——拆分前值得先确认每个符号的真实出处,避免把"文档说它在哪"当成"它实际在哪"。从当前源码结构看,类别定义已按提供商(Google/OpenAI/Anthropic/Kimi)进一步拆到 anthropic-categories.ts 等文件并汇总于 builtin-categories.ts,后者通过 buildCategoryRecord() 统一派生出 DEFAULT_CATEGORIESCATEGORY_PROMPT_APPENDSCATEGORY_DESCRIPTIONS 等记录——可以推断,这是 PR 拆分之后的继续演化:常量层被进一步下沉为"定义 + 派生"的结构,而 constants.ts 的 barrel 角色得以保留。

四、源码纵深:plan agent 提示词常量在 constants.ts 中的实现

拆分方案中 plan-agent-prompt.ts 承载的内容,在仓库中可以从 constants.ts 直接读到完整实现,这也是 654 行中"约 250 行 prompt-exempt"部分的主体:

  • PLAN_AGENT_SYSTEM_PREPEND_STATIC_BEFORE_SKILLS(第 21 行起):plan agent 的系统提示词前段。它强制要求 plan agent 在规划前执行"MANDATORY CONTEXT GATHERING PROTOCOL"——先用后台 explore/librarian 代理收集上下文,再输出用户请求摘要、不确定点与澄清问题,迭代到需求 100% 清晰;随后是三个强制输出章节:任务依赖图、并行执行波次(Parallel Execution Waves)、每个任务的 Category + Skills 推荐。
  • PLAN_AGENT_SYSTEM_PREPEND_STATIC_AFTER_SKILLS:提示词后段,规定计划的精确 Markdown 响应格式、<plan>...</plan> 交付物信封(Deliverable Envelope)规则——调用方靠这个信封区分最终交付物与探索性消息。
  • buildPlanAgentSystemPrepend(categories, skills)(第 332 行起):将"前段静态文本 + 动态渲染的 AVAILABLE CATEGORIES / AVAILABLE SKILLS 表格 + 后段静态文本"三段拼接为最终系统提示词,其中 renderPlanAgentCategoryRows / renderPlanAgentSkillRows 负责把运行时解析出的类别与技能渲染成 Markdown 表格行。

与之配套的"名称工具"部分(对应拆分方案中的 plan-agent-names.ts)也在同一文件中可见:

  • PLAN_AGENT_NAMES = ["plan"]isPlanAgent()第 347-356 行):大小写不敏感判定某 agent 是否应注入 plan 系统提示词;
  • PLAN_DELIVERABLE_TAG = "plan"getDeliverableTag():plan agent 交付物信封标签,与提示词中"必须用 <plan> 包裹"的指令一一对应——源码注释明确说明"与注入信封指令使用同一谓词门控,因此提取端只在提示词要求过标签时才期望它";
  • PLAN_FAMILY_NAMES = ["plan", "prometheus"]isPlanFamily()第 378-389 行):plan 家族共享"互斥委派阻塞 + task 工具权限",但共享系统提示词——注释特别强调"只有 isPlanAgent 控制提示词注入"。这正是把"名称判定"与"提示词文本"拆到不同模块的语义依据:两者的消费者和作用范围不同。

五、验证流程:拆分类重构的最小验证集

PR 的 Testing 一节给出了拆分类重构的验证标准,这套流程对所有"纯移动、不改语义"的重构都适用:

  1. bun run typecheck 通过——类型系统会捕获拆分中的漏导出、类型路径错误;
  2. bun test src/tools/delegate-task/ 通过——该目录下的既有测试(如 category-resolver.test.tsprompt-builder.test.ts 等)一行未动却全部通过,本身就是"行为零变化"的最强证据:测试断言的是模块行为,导入路径变了但行为必须一致;
  3. bun run build 成功——打包产物无断裂。

值得强调的是 PR 摘要中"all existing tests untouched"这个表述的方法论意义:如果一次纯移动重构需要修改任何测试,就说明改动已经越过"组织层面"进入了"行为层面",应当停下来审查。

六、可复用的重构模式总结

把这份 PR 抽象出来,就得到一套在大型 TypeScript 代码库中安全拆分"巨型 constants 文件"的标准动作:

  1. 先盘点职责:把大文件里的符号按"它服务哪个特性"分组,每组一个候选模块;
  2. 保留原文件名作 barrel:原 constants.ts 退化为纯 re-export(本次为 4 行),所有既有导入路径原样保留;
  3. 明确豁免项:提示词等不可分割的文本资产可以豁免行数规则,但要显式标注(prompt-exempt),而不是让规则形同虚设;
  4. 核对符号真实出处:拆分前确认每个符号实际定义位置,顺手清理过期文档声明(如 CATEGORY_MODEL_REQUIREMENTS 的归属);
  5. 以"测试零改动 + typecheck + build"作为验收线:13 处消费方零导入改动、既有测试原样通过,构成完整的兼容性证据链。

这套模式在当前仓库的 delegate-task 模块中已经历了进一步演化(内置类别定义下沉到按提供商拆分的定义文件 + builtin-categories.ts 派生记录),而 barrel 兼容层始终未被破坏——这正是桶式再导出作为"重构安全网"的长期价值所在。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
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
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384