首页
/ LobeHub 深度代码评审中的复用与架构维度:三层复用模型、三轮检查法与修复层归属判定

LobeHub 深度代码评审中的复用与架构维度:三层复用模型、三轮检查法与修复层归属判定

2026-09-04 23:44:57作者:魏侃纯Zoe

在 LobeHub 的多维度 AI 代码评审技能(deep-review)中,reuse-architecture 是唯一一个强制要求"全仓库搜索"的评审维度——它的核心问题是:这个 diff 是否在重复发明仓库已有的东西、是否无视既有实现范式、是否在侵蚀架构边界。本文完整拆解该维度文件(reuse-architecture.md)的三层复用模型、快速检查清单、三轮检查法(precedent / outward / inward)与"修复放错层"判定规则,并结合仓库中真实存在的分层结构与规则源文件,说明如何把一份跨文件思考的评审规则落地为可执行、可验证的工程实践。

一、这份文档在 LobeHub 评审体系中的位置

LobeHub 仓库内置了一套面向 AI 编码助手的技能体系(.agents/skills/),其中 deep-review 技能将代码评审拆分为十四个维度,每个维度一个独立规则文件,存放在 references/dimensions/ 目录下。reuse-architecture 的维度定义如下:

维度 覆盖范围
reuse-architecture 重复实现、未被使用的既有模式、可扩展性、架构边界

维度文件头部带有 frontmatter 元数据,声明了它的评审行为参数:

---
id_prefix: reuse        # 该维度产出的 finding ID 前缀
verify: true            # 该维度的发现必须经过独立 verify 子代理对抗性复核
skip_when: pure-deletion or docs-only diff   # 纯删除或纯文档 diff 时跳过
---

三个参数各有含义:

  • id_prefix: reuse:报告中该维度产出的每条发现(finding)都会以 reuse 为 ID 前缀,便于多代理场景下按维度归因与去重;
  • verify: true:在 deep 模式下,该维度的候选发现不能直接进入报告,必须经过独立 verify 子代理的逐条证伪(三态判定 confirmed / false_positive / need_more_context)。这一点在 deep-review 维度总表中对应 Verified? = yes
  • skip_when:仅在纯删除或纯文档 diff 时跳过。值得注意的是,deep-review 技能对"docs-only"有严格定义——面向人类阅读的散文才算文档;.agents/skills/**AGENTS.md / CLAUDE.md、提示词模板等"给代理执行的可执行指令"按代码对待,因此这类 diff 永远不会被当作 docs-only 跳过。

裁剪表(Pruning table)还规定:ai-coding-bad-habitscode-stylelogicbusiness-logicreuse-architecture 五个维度默认永不裁剪(仅 docs/lockfile-only diff 例外)。这体现了复用与架构检查在 LobeHub 评审体系中的核心地位:只要 diff 涉及代码,跨文件查重与边界检查就必须执行。

该维度的一个显著特征在文档开篇即被强调:

Cross-file thinking: does this diff reinvent something the repo already has, ignore an established pattern, or erode an architectural boundary? This is the only dimension whose findings require repo-wide searching — never judge from the diff alone.

其他维度往往只读 diff 与少量邻域即可判断,而 reuse-architecture 的结论必须建立在"整个仓库已经怎么解决这个问题"的基础上。后文的所有检查方法都服务于这一点。

二、复用的三层模型:Unit、Idiom、Cross-layer route

文档将"复用"划分为三个层级,并指出 AI 生成的 diff 最容易在上两层失守——它们写出局部自洽的代码,却从不检查仓库如何已解决这一类问题:

  1. Unit(单元级):一个已存在的函数 / hook / 组件 / 工具;
  2. Idiom(范式级):针对某类操作已确立的实现范式(idiom)。文档给出的典型例子是数据获取——正确做法是走 "store SWR hooks + service + lambdaClient" 管道,而不是 useEffect + useState
  3. Cross-layer route(跨层路由级):某一类功能如何贯穿各层(component → store → service → TRPC → repository),同类功能遵循同一条路由。

这三层在 LobeHub 仓库中都有真实对应的规则源,可以逐一印证:

Idiom 层的真实来源:data-fetching-architecture 技能。该技能规定了仓库唯一合法的服务端数据获取路由:

Component ──1. 调用 store 的 useFetchXxx hook──▶ Zustand Store (State + Hook)
          ──2. useClientDataSWR 调用 service──▶ Service Layer (xxxService)
          ──3. 调用 lambdaClient──▶ lambdaClient (TRPC Client)

其硬性约束包括:禁止用 useEffect 拉数据;禁止在组件或 store 中直接调用 lambdaClient;禁止用 useState 保存服务端数据;命名规范为读 hook 用 useFetchXxx、缓存失效助手用 refreshXxx。这正是 reuse-architecture 文档里"Idiom"一词的实体化:当 diff 中出现 useEffect(() => { lambdaClient.xxx.query().then(setData) }, []) 这类写法时,"偏离既有 idiom"的判断即成立,且 rule_source 应当指向该技能文件。

Cross-layer route 层的真实来源:project-overview 技能。该技能给出 LobeHub monorepo 的分层地图与数据流:

React UI → Store Actions → Client Service → TRPC Lambda → Server Services → DB Model → PostgreSQL

以及各层落位:UI 组件在 src/componentssrc/features、SPA 页面在 src/routes/(要求保持薄、委托给 features)、Zustand store 在 src/store、客户端服务在 src/services/、tRPC 路由在 apps/server/src/routers/、DB 三层(schema / model / repository)在 packages/database/。评审一个新功能"是否跳层、并层或发明新层"时,这张地图就是判据。

业务代码分层约束同样来自 project-overview:src/routes/ 下的页面段必须保持薄(thin),业务逻辑下沉到 src/features/——这一条同时出现在 reuse-architecture 的快速检查清单中(见下文第四节)。

三、快速检查清单(Quick checklist)逐条解读

文档的 Quick checklist 是 light 模式评审的唯一输入(light 模式评审员只读各维度的 Quick checklist 部分),也是 deep 模式评审员完整阅读的第一段。以下 11 条逐项给出含义与仓库内的检查落点:

  1. 新行为单元重复既有实现:diff 引入了新的文件、导出的 hook/util/component/selector,或一个"可命名"的 ≥ 20 行代码块,而仓库已有行为等价实现。默认查重路径:packages/utils/src/utils/src/hooks/ 及共享模块——这些目录在当前仓库中均实际存在(如 packages/utils/srcsrc/utilssrc/hooks),是 outward 路径的第一搜索目标。

  2. 手搓标准范式已覆盖的逻辑:ad-hoc 类型守卫、手动 setInterval + ref 清理、字符串拼接路径、自定义校验等。规则是"先搜索,再写码"——写这类代码前必须先 rg 确认仓库没有现成工具。

  3. Idiom 偏离:仓库对该操作类别有既定范式,但 diff 自创一套。文档列出三个具体形态:

    • useEffect / useState 拉服务端数据,而非 store SWR 管道(对应 data-fetching-architecture 技能 的 DON'T 清单);
    • 手搓 modal 状态,而非用 createModal(对应 modal 技能);
    • 组件绕过 service 直接调用 lambdaClient(违反"Service Layer 是唯一 lambdaClient 封装层"的原则)。
  4. 跨层路由偏离:新功能与最近的同类兄弟(sibling)结构不同——跳过一个层、合并两个层或发明新层——且没有书面理由。比较的基准是"最近的同类实现",不是抽象理想架构。

  5. 复制粘贴块:带轻微变化的重复代码块,本应是一个共享函数。

  6. 参数蔓延(Parameter sprawl):在既有函数上不断堆叠布尔/选项标志,而不是泛化接口或拆分函数。

  7. 抽象泄漏(Leaky abstraction):暴露了调用方本不应依赖的内部细节,或破坏了既有抽象边界。

  8. 修复放错层(Fix at wrong layer):在长管道中,修复所在的层与"拥有该问题的层"不匹配。文档区分两种典型错误:

    • 变体专属的怪癖被修在共享层:special-case 污染影响所有其他消费者;
    • 类别共性问题只修在一个变体里:症状式修复,同一条 bug 在其他所有路径上继续存活。

    该条与下文的 "Fix placement" 检查法对应,是 bug 修复类 diff 的重点。

  9. 裸字符串/数字:仓库已有对应 enum/constant 的地方,diff 却硬编码字面量。

  10. 手维护的平行目录(parallel catalog):菜单/Tab/配置列表在多个文件间复制。规则是"从单一来源派生",因为平行副本必然漂移——文档特别注明"设置分类目录(settings category catalog)已经因此丢过条目",这是一个来自仓库真实教训的注脚。

  11. 业务/领域代码放错层:例如 src/routes/ 下的页面段混入业务逻辑,违反"页面段保持薄、委托给 src/features/"的分层约定(project-overview 技能 的 Architecture Map 明确要求)。

四、规则源(Rule sources):deep 模式评审前必读

文档规定 deep 模式的评审代理在评审前必须先阅读对应规则源,把"仓库的既定标准"加载进上下文。reuse-architecture 的规则源清单如下(原文档中的相对链接已转换为仓库根路径):

这条规则背后是 deep-review 技能的核心原则之一——Rules over model:评审质量来自细粒度、可执行的维度规则,而非更聪明的模型。规则源就是"可执行规则"的实体。

五、三轮检查法:Precedent、Outward、Inward

"How to check" 一节规定了三遍检查,顺序固定:先 precedent(范式对齐),再 outward(查重与范式复用),最后 inward(可扩展性)

5.1 Precedent(范式对齐)——任何新功能/新能力先跑这一遍

四步流程:

  1. 分类:判定 diff 增加了什么(数据获取、store slice、service 方法、modal、路由、builtin tool、DB model……);
  2. 找最近同类:找到最近的同种既有实现——兄弟目录、相似的 store slice、做类似工作的 service——并通读它如何贯穿各层;
  3. 比结构,不比名字:分层是否相同?命名方案是否相同(useFetchXxx / refreshXxx)?失败处理形状是否相同?结构偏离即构成 finding,除非 diff 或 PR 说明了既有范式为何不适用
  4. 类别技能优先于最老代码:当类别模式技能存在时,以技能为准——既有的"先例"本身也可能是遗留(legacy)代码。

注意第 4 步与 "Not violations" 一节首尾呼应:遵循较新的技能而偏离较老的代码是正确行为,不构成违规。

5.2 Outward(去重 + 范式复用)

五步流程,是"repo-wide searching" 要求的具体化:

  1. 列出本 diff 引入的所有行为单元(behavior units);
  2. 对每个单元,用 rg 以"动作词 + 上下文词"组合搜索。文档给出的示例组合:window.open + popup、setInterval + poll、JSON.parse + storage——即不要只搜函数名,要搜"做什么"的语义组合;
  3. 默认复用源优先检查packages/utils/src/utils/src/hooks/src/lib/*/store/selectors/,以及被改动文件的兄弟目录;
  4. 打开每一个命中并比较行为等价性:相同输入 → 相同输出/副作用。两个关键的判等规则:
    • 名字/参数不同仍然算等价(换皮不算新实现);
    • 语法相似但语义不同不算等价(不能仅凭长得像就报重复);
  5. 只有 existing_implementations 字段填写完整才允许上报:格式为 file:linefile:line-range,至少 1 条。没有具体到行的既有实现证据,就不构成该维度的发现。

第 5 步是一个重要的输出纪律:reuse 维度的每条发现都必须附带可定位的既有实现坐标,这让后续的 verify 环节与人类复核都能直接跳转验证。

5.3 Inward(可扩展性)

审视 diff 自身的设计质量:参数蔓延、抽象泄漏、硬编码字面量(需先 rg 确认对应常量确实存在,再报"应使用既有常量")。与 outward 相对,inward 不看仓库其他地方,只看这份代码自身是否把未来变更的成本转嫁给了下一个人。

5.4 Fix placement:长管道上 bug 修复的层归属

对长管道上的 bug 修复(例如"用户消息 → agent runtime → provider → 渲染"这条链),同一处 bug 往往可以在多个点打补丁。文档规定:正确的落点由"问题归属"决定,而不是"哪里改起来最方便"。四步判定:

  1. 识别管道,列出该修复可能落位的所有候选层;
  2. 提问:根因是某一个变体专属(某个 provider、某个平台、某个客户端),还是整个类别共有
  3. 归属判定的两个方向:
    • 变体专属 → 修复落在该变体自己的层。文档给出的真实例子:DeepSeek 的参数怪癖应修在 packages/model-runtime/src/providers/deepseek/,而不是修在共享的 core/openaiCompatibleFactory/——后者一旦为单个 provider 加分支,就为所有 provider 引入了分支;
    • 类别共有 → 修复落在共享层,而不是在单个调用点复制一份;
  4. 先检查共享层既有的扩展点(factory options、hooks、per-variant config):仓库通常已为变体行为预留了接缝(seam)。在接缝已存在时仍往共享代码里加 if (variant === ...) 分支,本身就是一种违规。

该规则与 ai-coding-bad-habits 维度中的"Narrow special case inside generic code"检查项互为表里:后者从"通用代码里嵌窄分支"的代码形态切入,fix placement 从"bug 归属"切入,但判定标准一致——变体策略留在拥有它的层。两个维度还明确约定不得对同一根因重复上报,避免报告噪音。

5.5 大仓库回退策略

文档给出了一条务实的性能护栏:单次 rg 若超过约 30 秒,则把搜索范围收缩为"被改动文件的顶层目录 + 默认复用源"。这保证了即便在 LobeHub 这类包含约 80 个 workspace 包的大型 monorepo 中,该维度也能在合理时间内完成全量检查。

六、什么算违规(Violations)

只有以下四类情形才构成 reuse-architecture 的正式 finding:

  1. 引入新单元且存在 ≥ 1 个行为等价的既有实现。注意措辞:即使那份旧拷贝本身很古老,nature 仍记为 "introduced"——因为"添加重复"这个动作是本次 diff 新造的;
  2. 为一个仓库已有既定 idiom 或跨层范式的类别发明了新的实现路线。此时 existing_implementations 指向先例实现(若存在类别技能,一并引用为 rule_source);
  3. 修复的层与问题归属矛盾:变体怪癖进了共享代码,或类别 bug 只修了一个变体。finding 必须点名拥有问题的层;若改动的是共享层,还必须指出被绕过的既有扩展接缝
  4. 扩展了易重复模式:例如为某目录(catalog)添加了第三份手维护的副本。

这四条与 "Not violations" 清单共同划定了判定边界:

  • 本 diff 未触碰、也未扩展的旧文件之间的既有重复——不报;
  • 与本 PR 无关的全仓库可扩展性感慨("这个项目应该有个通用 useInterval")——超范围,不报;
  • 有书面理由的刻意重复:注释或 PR 描述解释了为何此处不该共享——不报;
  • 偏离的先例本身就是遗留:当技能或迁移指南已标记更新做法时,遵循技能、违背旧代码是正确行为;
  • 共享层修复单变体报告的 bug:当根因确实位于共享代码时(报告者恰好是第一个踩中的变体)——先核实归属再报 placement,避免误判。

这套"违规 / 非违规"的双清单设计,正是 deep-review 核心原则 Calibrate to codebase and lifespan(按代码库现状校准、按代码寿命校准)的落地:评审基准是"仓库已达成的标准",而不是理想化标准。

七、对抗性验证:reuse 发现的二次闸门

verify: true 意味着 reuse 维度的每条候选发现在进入报告前,必须通过独立 verify 子代理的复核。针对 reuse-architecture 的查重类发现,验证规则有专门的增补文件 verification/reuse-architecture.md

  1. 打开 existing_implementations 中的每一条,带足上下文比较行为:
    • 至少一个实现具有等价的输入、输出与副作用 → 判定 confirmed
    • 所有实现只是语法相似 → 判定 false_positive,且必须写明语义差异;
    • 所需上下文不可得 → 判定 need_more_context
  2. reuse-architecture 的发现默认 can_auto_fix: false:因为"迁移到既有实现"会改变所有调用方(blast radius 扩大)。唯一例外——证据表明仅是常量替换或单次 import 替换且无签名变更时,才可标记为可自动修复。

三态判定(而非置信度百分比)是 deep-review 的反幻觉设计:校准式的分数不可靠,可证伪的判定才是可靠的过滤器。而第 2 条默认关闭自动修复,则体现了对"复用重构"这类看似机械、实则会连锁影响调用方变更的谨慎。

最终报告中,reuse 查重类发现会以 **Existing implementations** 行渲染全部既有实现坐标(见 report-template.md 的渲染规则),与 rule_source 行一起,构成从"结论 → 仓库证据 → 规则依据"的完整引用链。

八、实践要点小结

把这份维度文件浓缩为可操作的工作流,可以归纳为:

  1. 先分类,再比较:新功能 diff 先走 Precedent 路径——找最近的同类兄弟、比结构不比名字、有类别技能时以技能为标尺;
  2. 查重必须语义级 + 有坐标rg 用"动作 + 上下文"组合,默认查 packages/utils/src/utils/src/hooks/ 与兄弟目录,报重复前必须填上 file:line 级别的 existing_implementations
  3. 数据获取类改动对照标准路由:component → store SWR hook(useFetchXxx)→ service → lambdaClient,任何 useEffect 拉数据、组件直调 lambdaClient、手搓 modal 状态都是 idiom 偏离;
  4. bug 修复先问归属:变体怪癖回变体层(如 deepseek provider),类别问题进共享层,且优先使用共享层既有扩展接缝而非新增 if (variant === ...)
  5. 克制即纪律:旧代码的既有重复、无 PR 关联的可扩展性感慨、有书面理由的重复都不报;被验证环节推翻的"语法相似"不构成重复。

这套规则的价值在于:它把"这段代码在仓库里是否自洽"这一传统上依赖资深评审者直觉的判断,转化为一组可被 AI 子代理机械执行、可被独立验证环节证伪、且每条结论都携带仓库内证据坐标的检查步骤——这正是 LobeHub 将评审能力沉淀为 .agents/skills/ 中可版本化、可演进规则文件的核心思路。

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