LobeHub 深度代码评审中的复用与架构维度:三层复用模型、三轮检查法与修复层归属判定
在 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-habits、code-style、logic、business-logic、reuse-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 最容易在上两层失守——它们写出局部自洽的代码,却从不检查仓库如何已解决这一类问题:
- Unit(单元级):一个已存在的函数 / hook / 组件 / 工具;
- Idiom(范式级):针对某类操作已确立的实现范式(idiom)。文档给出的典型例子是数据获取——正确做法是走 "store SWR hooks + service +
lambdaClient" 管道,而不是useEffect+useState; - 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/components 与 src/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 条逐项给出含义与仓库内的检查落点:
-
新行为单元重复既有实现:diff 引入了新的文件、导出的 hook/util/component/selector,或一个"可命名"的 ≥ 20 行代码块,而仓库已有行为等价实现。默认查重路径:
packages/utils/、src/utils/、src/hooks/及共享模块——这些目录在当前仓库中均实际存在(如 packages/utils/src、src/utils、src/hooks),是 outward 路径的第一搜索目标。 -
手搓标准范式已覆盖的逻辑:ad-hoc 类型守卫、手动
setInterval+ ref 清理、字符串拼接路径、自定义校验等。规则是"先搜索,再写码"——写这类代码前必须先rg确认仓库没有现成工具。 -
Idiom 偏离:仓库对该操作类别有既定范式,但 diff 自创一套。文档列出三个具体形态:
- 用
useEffect/useState拉服务端数据,而非 store SWR 管道(对应 data-fetching-architecture 技能 的 DON'T 清单); - 手搓 modal 状态,而非用
createModal(对应 modal 技能); - 组件绕过 service 直接调用
lambdaClient(违反"Service Layer 是唯一 lambdaClient 封装层"的原则)。
- 用
-
跨层路由偏离:新功能与最近的同类兄弟(sibling)结构不同——跳过一个层、合并两个层或发明新层——且没有书面理由。比较的基准是"最近的同类实现",不是抽象理想架构。
-
复制粘贴块:带轻微变化的重复代码块,本应是一个共享函数。
-
参数蔓延(Parameter sprawl):在既有函数上不断堆叠布尔/选项标志,而不是泛化接口或拆分函数。
-
抽象泄漏(Leaky abstraction):暴露了调用方本不应依赖的内部细节,或破坏了既有抽象边界。
-
修复放错层(Fix at wrong layer):在长管道中,修复所在的层与"拥有该问题的层"不匹配。文档区分两种典型错误:
- 变体专属的怪癖被修在共享层:special-case 污染影响所有其他消费者;
- 类别共性问题只修在一个变体里:症状式修复,同一条 bug 在其他所有路径上继续存活。
该条与下文的 "Fix placement" 检查法对应,是 bug 修复类 diff 的重点。
-
裸字符串/数字:仓库已有对应 enum/constant 的地方,diff 却硬编码字面量。
-
手维护的平行目录(parallel catalog):菜单/Tab/配置列表在多个文件间复制。规则是"从单一来源派生",因为平行副本必然漂移——文档特别注明"设置分类目录(settings category catalog)已经因此丢过条目",这是一个来自仓库真实教训的注脚。
-
业务/领域代码放错层:例如
src/routes/下的页面段混入业务逻辑,违反"页面段保持薄、委托给src/features/"的分层约定(project-overview 技能 的 Architecture Map 明确要求)。
四、规则源(Rule sources):deep 模式评审前必读
文档规定 deep 模式的评审代理在评审前必须先阅读对应规则源,把"仓库的既定标准"加载进上下文。reuse-architecture 的规则源清单如下(原文档中的相对链接已转换为仓库根路径):
- project-overview/SKILL.md —— 分层地图:apps / packages / src 各放什么;
- spa-routes/SKILL.md —— routes(根页面段)与 features 的划分;
- data-fetching-architecture/SKILL.md —— 服务端数据的标准跨层路由(component → store SWR hook → service →
lambdaClient); - store-data-structures/SKILL.md 与 zustand/SKILL.md —— 当 diff 触碰 store 时,store 形状与 action 范式的判据;
- 类别模式技能:当 diff 的类别存在对应技能(
modal、trpc-router、builtin-tool、drizzle……)时,到.agents/skills/下查找匹配改动域的技能——一旦存在,它就是"标尺"(yardstick),并应在 finding 的rule_source字段中引用。当前仓库中这些技能目录均实际存在(如 trpc-router/SKILL.md、drizzle/SKILL.md、builtin-tool/SKILL.md)。
这条规则背后是 deep-review 技能的核心原则之一——Rules over model:评审质量来自细粒度、可执行的维度规则,而非更聪明的模型。规则源就是"可执行规则"的实体。
五、三轮检查法:Precedent、Outward、Inward
"How to check" 一节规定了三遍检查,顺序固定:先 precedent(范式对齐),再 outward(查重与范式复用),最后 inward(可扩展性)。
5.1 Precedent(范式对齐)——任何新功能/新能力先跑这一遍
四步流程:
- 分类:判定 diff 增加了什么(数据获取、store slice、service 方法、modal、路由、builtin tool、DB model……);
- 找最近同类:找到最近的同种既有实现——兄弟目录、相似的 store slice、做类似工作的 service——并通读它如何贯穿各层;
- 比结构,不比名字:分层是否相同?命名方案是否相同(
useFetchXxx/refreshXxx)?失败处理形状是否相同?结构偏离即构成 finding,除非 diff 或 PR 说明了既有范式为何不适用; - 类别技能优先于最老代码:当类别模式技能存在时,以技能为准——既有的"先例"本身也可能是遗留(legacy)代码。
注意第 4 步与 "Not violations" 一节首尾呼应:遵循较新的技能而偏离较老的代码是正确行为,不构成违规。
5.2 Outward(去重 + 范式复用)
五步流程,是"repo-wide searching" 要求的具体化:
- 列出本 diff 引入的所有行为单元(behavior units);
- 对每个单元,用
rg以"动作词 + 上下文词"组合搜索。文档给出的示例组合:window.open+ popup、setInterval+ poll、JSON.parse+ storage——即不要只搜函数名,要搜"做什么"的语义组合; - 默认复用源优先检查:
packages/utils/、src/utils/、src/hooks/、src/lib/、*/store/selectors/,以及被改动文件的兄弟目录; - 打开每一个命中并比较行为等价性:相同输入 → 相同输出/副作用。两个关键的判等规则:
- 名字/参数不同仍然算等价(换皮不算新实现);
- 语法相似但语义不同不算等价(不能仅凭长得像就报重复);
- 只有
existing_implementations字段填写完整才允许上报:格式为file:line或file:line-range,至少 1 条。没有具体到行的既有实现证据,就不构成该维度的发现。
第 5 步是一个重要的输出纪律:reuse 维度的每条发现都必须附带可定位的既有实现坐标,这让后续的 verify 环节与人类复核都能直接跳转验证。
5.3 Inward(可扩展性)
审视 diff 自身的设计质量:参数蔓延、抽象泄漏、硬编码字面量(需先 rg 确认对应常量确实存在,再报"应使用既有常量")。与 outward 相对,inward 不看仓库其他地方,只看这份代码自身是否把未来变更的成本转嫁给了下一个人。
5.4 Fix placement:长管道上 bug 修复的层归属
对长管道上的 bug 修复(例如"用户消息 → agent runtime → provider → 渲染"这条链),同一处 bug 往往可以在多个点打补丁。文档规定:正确的落点由"问题归属"决定,而不是"哪里改起来最方便"。四步判定:
- 识别管道,列出该修复可能落位的所有候选层;
- 提问:根因是某一个变体专属(某个 provider、某个平台、某个客户端),还是整个类别共有?
- 归属判定的两个方向:
- 变体专属 → 修复落在该变体自己的层。文档给出的真实例子:DeepSeek 的参数怪癖应修在 packages/model-runtime/src/providers/deepseek/,而不是修在共享的 core/openaiCompatibleFactory/——后者一旦为单个 provider 加分支,就为所有 provider 引入了分支;
- 类别共有 → 修复落在共享层,而不是在单个调用点复制一份;
- 先检查共享层既有的扩展点(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 个行为等价的既有实现。注意措辞:即使那份旧拷贝本身很古老,
nature仍记为"introduced"——因为"添加重复"这个动作是本次 diff 新造的; - 为一个仓库已有既定 idiom 或跨层范式的类别发明了新的实现路线。此时
existing_implementations指向先例实现(若存在类别技能,一并引用为rule_source); - 修复的层与问题归属矛盾:变体怪癖进了共享代码,或类别 bug 只修了一个变体。finding 必须点名拥有问题的层;若改动的是共享层,还必须指出被绕过的既有扩展接缝;
- 扩展了易重复模式:例如为某目录(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:
- 打开
existing_implementations中的每一条,带足上下文比较行为:- 至少一个实现具有等价的输入、输出与副作用 → 判定
confirmed; - 所有实现只是语法相似 → 判定
false_positive,且必须写明语义差异; - 所需上下文不可得 → 判定
need_more_context;
- 至少一个实现具有等价的输入、输出与副作用 → 判定
- reuse-architecture 的发现默认
can_auto_fix: false:因为"迁移到既有实现"会改变所有调用方(blast radius 扩大)。唯一例外——证据表明仅是常量替换或单次 import 替换且无签名变更时,才可标记为可自动修复。
三态判定(而非置信度百分比)是 deep-review 的反幻觉设计:校准式的分数不可靠,可证伪的判定才是可靠的过滤器。而第 2 条默认关闭自动修复,则体现了对"复用重构"这类看似机械、实则会连锁影响调用方变更的谨慎。
最终报告中,reuse 查重类发现会以 **Existing implementations** 行渲染全部既有实现坐标(见 report-template.md 的渲染规则),与 rule_source 行一起,构成从"结论 → 仓库证据 → 规则依据"的完整引用链。
八、实践要点小结
把这份维度文件浓缩为可操作的工作流,可以归纳为:
- 先分类,再比较:新功能 diff 先走 Precedent 路径——找最近的同类兄弟、比结构不比名字、有类别技能时以技能为标尺;
- 查重必须语义级 + 有坐标:
rg用"动作 + 上下文"组合,默认查packages/utils/、src/utils/、src/hooks/与兄弟目录,报重复前必须填上file:line级别的existing_implementations; - 数据获取类改动对照标准路由:component → store SWR hook(
useFetchXxx)→ service →lambdaClient,任何useEffect拉数据、组件直调lambdaClient、手搓 modal 状态都是 idiom 偏离; - bug 修复先问归属:变体怪癖回变体层(如 deepseek provider),类别问题进共享层,且优先使用共享层既有扩展接缝而非新增
if (variant === ...); - 克制即纪律:旧代码的既有重复、无 PR 关联的可扩展性感慨、有书面理由的重复都不报;被验证环节推翻的"语法相似"不构成重复。
这套规则的价值在于:它把"这段代码在仓库里是否自洽"这一传统上依赖资深评审者直觉的判断,转化为一组可被 AI 子代理机械执行、可被独立验证环节证伪、且每条结论都携带仓库内证据坐标的检查步骤——这正是 LobeHub 将评审能力沉淀为 .agents/skills/ 中可版本化、可演进规则文件的核心思路。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00