首页
/ Understand-Anything 深度解析:/understand-explain 技能如何在知识图谱中精准定位并讲解任意组件

Understand-Anything 深度解析:/understand-explain 技能如何在知识图谱中精准定位并讲解任意组件

2026-09-06 13:57:41作者:裴锟轩Denise

/understand-explain 是 Understand-Anything 插件内置的技能之一,用于对代码库中的某个文件、函数或模块给出「有架构上下文」的深度讲解。它不靠凭空阅读代码,而是先查由 /understand 生成的 knowledge-graph.json,从图谱中取出目标节点、邻接边、所属架构层,再结合源码给出解释。读完本文,你将掌握该技能的完整工作流程(数据目录解析、图谱新鲜度校验、节点/边/层定位、源码深读)、知识图谱 JSON 的数据契约,以及每一步在插件源码中的对应实现。

技能定位与输入格式

技能的元数据定义在 SKILL.md 的 frontmatter 中:

---
name: understand-explain
description: Use when you need a deep-dive explanation of a specific file, function, or module in the codebase
argument-hint: "[file-path]"
---

它接收一个可选参数($ARGUMENTS),支持两种写法:

  • 文件路径,如 src/auth/login.ts:解释整个文件级组件;
  • 函数/组件记法 path:funcName,如 src/auth/login.ts:verifyToken:解释该文件下的某个具体函数。

插件中还有一套程序化的等价实现 explain-builder.ts,其 buildExplainContext 函数注释明确说明支持这两种格式,并用 lastIndexOf(":") 拆分路径与函数名(同时排除含 :// 的误判),先按 filePath + name 精确匹配函数节点,匹配不到再回退为按 filePath 匹配文件节点。

数据契约:知识图谱 JSON 结构

/understand-explain 的一切查询都发生在 $UA_DIR/knowledge-graph.json 上。SKILL.md 中的 "Graph Structure Reference" 一节给出了该文件的核心结构,必须完整理解它才能高效使用本技能:

  • project{name, description, languages, frameworks, analyzedAt, gitCommitHash}
  • nodes[] — 每个节点含 {id, type, name, filePath?, summary, tags[], complexity, languageNotes?}
    • 代码节点类型:filefunctionclassmoduleconcept
    • 非代码节点类型:configdocumentservicetableendpointpipelineschemaresource
    • 领域/知识节点类型:domainflowsteparticleentitytopicclaimsource
    • 节点 ID 以类型作前缀,例如 file:pathfunction:path:nameconfig:patharticle:path
  • edges[] — 每条边含 {source, target, type, direction, weight}
    • 常用边类型:importscontainscallsdepends_onconfiguresdocumentsdeploystriggerscontains_flowflow_steprelatedcites
  • layers[] — 每个含 {id, name, description, nodeIds[]}
  • tour[] — 每个含 {order, title, description, nodeIds[]}

仓库中可以直接查看一份真实样本:仪表盘自带的 knowledge-graph.json(97 个节点、183 条边、7 个架构层、12 个 tour 步骤),其中 project.gitCommitHash 字段正是新鲜度校验的锚点,layers 里的 "Types & Schema Layer" 这类条目就是 /understand-explain 第 6 步要查的内容。

与类型定义的交叉验证

上述 JSON 结构在 core 包中有严格的 TypeScript 类型与运行时校验,可以作为「实现事实」来核对:

  • types.ts 定义了完整的枚举:NodeType 共 27 种(5 种代码 + 8 种非代码 + 3 种领域 + 5 种知识 + 6 种设计),EdgeType 共 38 种、分 9 个类别(结构、行为、数据流、依赖、语义、基础设施、领域、知识、设计)——SKILL.md 里列出的只是「常用子集」。GraphNode 还有 lineRange?: [number, number](函数级节点的起止行号)和 complexity: "simple" | "moderate" | "complex" 三档取值;GraphEdgedirectionforward | backward | bidirectionalweight 为 0–1 的数值,另有可选 description
  • schema.ts 用 Zod 定义 KnowledgeGraphSchemavalidateGraph 会做别名归一(如 funcfunctionmethodfunctionusesdepends_on)、缺省值自动修正(边缺 weight 默认 0.5、节点缺 complexity 默认 moderate)、引用完整性检查(source/target 必须指向存在的节点,否则该边被丢弃)。这意味着技能里 Grep 到的节点类型/边类型都可以假定是「已归一化的规范值」。

高效读取策略:先 Grep,不要整图入上下文

SKILL.md 的 "How to Read Efficiently" 一节给出了四条检索纪律,这也是本技能区别于「直接把整个 JSON 丢给模型」的关键设计:

  1. 读文件之前先用 Grep 在 JSON 中搜索相关条目,定位后再精确读取;
  2. 只读需要的片段,绝不把整张图谱倾倒进上下文;
  3. namesummary 是最有用的字段——summary 是分析阶段由 LLM 写成的组件级描述,是快速理解节点语义的第一手材料;
  4. 边告诉你组件如何连接——顺着 importscalls 边追踪依赖链。

完整工作流程:从解析数据目录到输出解释

以下步骤完整继承自 SKILL.md 的 Instructions 一节。

第 1 步:解析数据目录 $UA_DIR

UA_DIR=$([ -d .understand-anything ] && echo .understand-anything || echo .ua)

规则是:旧的 .understand-anything/ 目录已存在时沿用旧目录,否则使用新的 .ua/ 目录。然后确认 $UA_DIR/knowledge-graph.json 存在;若不存在,提示用户先运行 /understand 生成图谱。

这一「双目录」规则与源码一致:persistence/index.ts 中的 resolveUaDirName 实现了同样的逻辑——旧项目无迁移直接原地读写,新项目写入 .ua/。该文件还负责在落盘前把节点 filePath 统一改写为相对项目根的路径(绝对路径转相对、项目外绝对路径只保留文件名),因此图谱里所有 filePath 都是可直接用来读源文件的相对路径——这正是第 7 步「按 filePath 读源码」能成立的前提。

第 2 步:使用图谱上下文前先校验新鲜度

这是本技能中最容易被忽视、但对结论可信度最关键的一步。流程如下:

  1. 从图谱元数据读取 project.gitCommitHash 作为 GRAPH_COMMIT_RAW,先把它解析为有效提交,再与 HEAD 比较,并检查项目范围内的已提交与工作区变更:
GRAPH_COMMIT=$(git rev-parse --verify --end-of-options "${GRAPH_COMMIT_RAW}^{commit}" 2>/dev/null)
git rev-parse HEAD
git diff --name-only "$GRAPH_COMMIT" HEAD -- .
git diff --cached --name-only -- .
git diff --name-only -- .
git ls-files --others --exclude-standard -- .
  1. -- . 路径约束是必需的:在 monorepo 中,只触碰兄弟子项目的提交不应让本项目的图谱被判为过期;哈希不一致本身也不构成过期,只有「项目内 diff 为空」时才算新鲜。
  2. 所有命令的输出中都忽略所选数据目录.ua/ 或旧的 .understand-anything/),因为里面是生成的图谱产物,不属于项目源码漂移。
  3. 若已提交 diff 或任一工作区命令报告了项目文件变更,在解释之前先警告:图谱上下文可能遗漏这些变更,并建议运行 /understand 刷新图谱。
  4. 仅当 GRAPH_COMMIT_RAW 解析成功时才执行 commit diff;如果图谱提交哈希缺失、非法或不可用,给出简短的尽力而为(best-effort)警告后继续,而不是阻塞流程。

这套 shell 流程与 core 包中的程序化实现 staleness.ts 是同一套语义:其中 PROJECT_PATHSPEC 常量显式排除了 .(understand-anything|ua) 目录(与第 3 点一一对应);getGraphFreshness 返回 fresh / dirty / stale / unknown 四种状态,unknown 与「新鲜」被刻意区分——Git 元数据读不到时应「软警告」而非声称图谱是最新的(对应第 5 点的 best-effort 原则);stale 状态还会给出 behind / ahead / diverged 三种分叉关系和 commitsAhead/commitsBehind 计数。所有 Git 调用带 5 秒超时,超时同样落入 unknown 而不阻塞。

第 3 步:定位目标节点

用 Grep 在知识图谱中搜索 $ARGUMENTS 对应的组件:

  • 文件路径(如 src/auth/login.ts):搜索 "filePath" 字段命中;
  • 函数记法(如 src/auth/login.ts:verifyToken):在限定文件路径的前提下搜索 "name" 字段中的函数名。

记下节点的精确 idtypesummarytagscomplexity——这几个字段是后续解释的信息骨架。

第 4 步:找出所有相连的边

用 Grep 在 edges 区域搜索目标节点 ID:

  • "source" 命中 → 该节点调用/导入/依赖的对象(出边);
  • "target" 命中 → 调用/导入/依赖该节点的对象(入边);
  • 记下相连节点 ID 与边类型。

第 5 步:读取相连节点,构建「邻域」

对第 4 步拿到的每个相连节点 ID,在 nodes 区域 Grep 出它们的 namesummarytype。这一步组装出目标组件的一跳邻域(neighborhood)——解释时「它和谁交互」的答案全部来自这里。

第 6 步:确定架构层

"layers" 区域 Grep 目标节点 ID,找到它属于哪个架构层以及该层的描述。层的 description 直接回答「这个组件在架构中扮演什么角色、为什么存在」。

第 7 步:读取真实源码

打开节点 filePath 指向的源文件,做深度分析。图谱提供结构与关系,但数据流、具体实现细节必须来自源码本身。

第 8 步:在上下文中解释组件

SKILL.md 规定解释必须覆盖五个维度:

  1. 架构中的角色——属于哪一层、为什么存在;
  2. 内部结构——它包含的函数/类(来自 contains 边);
  3. 外部连接——它导入什么、谁调用它、它依赖什么(来自边);
  4. 数据流——输入 → 处理 → 输出(来自源码);
  5. 清晰表达——假设读者可能不熟悉该编程语言,并点出值得理解的模式、惯用法与复杂点。

源码级印证:explain-builder 如何复刻这套流程

explain-builder.ts 是上述工作流的程序化版本,两者逐项对应,可作为「从源码结构看」的权威参照:

  • buildExplainContext(graph, path):先按 path:func 格式匹配函数节点,再回退文件匹配(对应第 3 步);通过 contains 边找出子节点(文件包含的函数/类,对应第 8 步的内部结构);对全部相关 ID 遍历一遍边,收集一跳邻居作为 connectedNodes、收集全部相关边作为 relevantEdges(对应第 4、5 步);最后在 layers 中查找包含目标节点 ID 的层(对应第 6 步)。
  • formatExplainPrompt(ctx):把上下文渲染成结构化的 Markdown prompt——# Deep Dive: <name> 标题下给出 Type/Complexity/File/Lines 元信息,随后是 ## Architectural Layer## Internal Components## Connected Components## Relationships(以 src --[edgeType]--> tgt 形式列出,跳过 contains 边)和 ## Language Notes 小节,末尾附上与第 8 步一致的 5 条解释指令。目标未找到时则输出 "Component Not Found" 提示,并给出三种可能原因(尚未分析——先跑 /understand、路径写法不同、文件已被删除或重命名)。

对应的测试 explain-builder.test.ts 用一份小型样本图谱(auth.ts 文件节点 + login/verify 函数节点 + db.ts 数据库文件节点,含 containsreads_from 边和 "Auth Layer" 层)验证了关键行为:按路径找到文件节点、childNodes 包含两个函数、connectedNodes 包含 file:src/db.tslayer.name 为 "Auth Layer"、未知路径返回 targetNode: nullsrc/auth.ts:login 记法命中函数节点,以及渲染结果包含 "Auth Layer" 等内容。该测试同时给出了一个可直接模仿的迷你图谱样本,适合读者本地演练 Grep 定位流程。

使用要点与常见坑

  • 前提条件:必须先成功运行过 /understand(其选项如 --full--language <lang>--exclude <patterns> 等定义在 understand/SKILL.md)。没有 $UA_DIR/knowledge-graph.json 时,本技能只会提示你先生成。
  • 过期图谱:第 2 步的警告意味着解释内容可能遗漏未提交或未分析的变更;看到警告后运行 /understand 刷新再解释,结论才可靠。core 包中还有 mergeGraphUpdatestaleness.ts 末尾)负责增量刷新——删除变更文件的旧节点、清理悬挂边、写入新 gitCommitHashanalyzedAt,因此图谱的 project.gitCommitHash 会随刷新前进。
  • monorepo 场景-- . 路径约束与 staleness.tsPROJECT_PATHSPEC 共同保证「兄弟项目变更不误伤本项目图谱新鲜度」,在多包仓库中使用本技能时尤其要保留该约束。
  • 字段语义complexity 只有三档取值(simple/moderate/complex)、weight 是 0–1 权重而非次数、边方向由 direction 字段表达——这些细节在 schema 校验层已被强制归一,解释时可以直接引用。

总结来说,/understand-explain 的设计精髓在于「图谱导航 + 源码实证」的分工:JSON 图谱负责回答「它是什么、属于哪层、和谁相连」,源码负责回答「它具体怎么运作」;而 Grep 优先的读取纪律与 Git 新鲜度校验,则分别保证了上下文成本可控与结论可信。

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