首页
/ Understand-Anything /understand-domain 技能深度解析:从代码库中自动提取业务领域知识并生成可交互流程图谱

Understand-Anything /understand-domain 技能深度解析:从代码库中自动提取业务领域知识并生成可交互流程图谱

2026-09-06 13:49:00作者:郦嵘贵Just

在大型代码库中,结构依赖图能回答“谁调用了谁”,却回答不了“这段代码在做什么业务”。Understand-Anything 的 /understand-domain 技能正是为此设计:它通过一个 Skill 工作流加一个轻量 Python 预处理器,从代码库中提取业务领域(Domain)、业务流程(Flow)与流程步骤(Step)三级知识,生成独立的 domain-graph.json,并在 Dashboard 中以横向领域流程图呈现。读完本文,你可以完整掌握该技能的六阶段工作流、两条分析路径(轻量扫描 / 从既有知识图谱派生)、预处理器 extract-domain-context.py 的扫描策略与限额设计,以及领域图谱的 JSON 结构与校验持久化机制。

Understand-Anything 领域视图界面演示:以横向流程图展示业务领域、流程与步骤

工作机制总览:两条路径,同一份输出

/understand-domain 的核心设计(见 SKILL.md)是“两条路径、同一产物”:

  • 路径 2(派生自既有知识图谱):如果项目中已存在知识图谱(.ua/knowledge-graph.json,或当旧目录存在时的 .understand-anything/knowledge-graph.json),则直接从图谱中派生领域知识——节点、边、分层、Tour 已包含摘要与标签,因此这一步几乎零成本,不需要重新扫描任何源文件;
  • 路径 1(轻量扫描):如果没有知识图谱,则执行一次轻量扫描——文件树 + 入口点检测 + 抽样文件,产出“原材料”交给 LLM 分析;
  • --full 参数:即使存在知识图谱,也会强制走一次全新扫描。

这套设计出自项目的设计文档 2026-04-01-business-domain-knowledge-design.md,其动机是:文件级依赖图的价值有限——导入关系在 IDE 里本来就可见;真正稀缺的是内嵌在代码中的业务逻辑与领域概念。设计文档估算轻量扫描路径的 token 成本约为完整 /understand 扫描的 10%–20%。

Phase 0:解析 PROJECT_ROOT、UA_DIR 与插件根目录

这是整个技能中最容易被忽视、却决定产物能否留存的一步。

0.1 Worktree 重定向:防止产物随临时工作区销毁

PROJECT_ROOT 位于 git worktree(而非主检出)内时,输出会被重定向到主仓库根目录。原因是由 Claude Code 管理的 worktree 是临时性的——会话结束后 worktree 被销毁,写在其中的数据目录(.ua/ 或旧版 .understand-anything/)连同领域图谱一起丢失。SKILL.md 给出的检测方法是:对比 git rev-parse --git-dirgit rev-parse --git-common-dir——在普通检出或子模块中两者解析到同一路径,在 worktree 中两者不同,且 --git-common-dir 的父目录即主仓库根。关键片段(引自 SKILL.md):

COMMON_DIR=$(git -C "$PROJECT_ROOT" rev-parse --git-common-dir 2>/dev/null)
GIT_DIR=$(git -C "$PROJECT_ROOT" rev-parse --git-dir 2>/dev/null)
if [ -n "$COMMON_DIR" ] && [ -n "$GIT_DIR" ]; then
  COMMON_ABS=$(cd "$PROJECT_ROOT" && cd "$COMMON_DIR" 2>/dev/null && pwd -P)
  GIT_ABS=$(cd "$PROJECT_ROOT" && cd "$GIT_DIR" 2>/dev/null && pwd -P)
  if [ -n "$COMMON_ABS" ] && [ "$COMMON_ABS" != "$GIT_ABS" ]; then
    MAIN_ROOT=$(dirname "$COMMON_ABS")
    if [ -d "$MAIN_ROOT" ] && [ "${UNDERSTAND_NO_WORKTREE_REDIRECT:-0}" != "1" ]; then
      echo "[understand-domain] Detected git worktree at $PROJECT_ROOT"
      echo "[understand-domain] Redirecting output to main repo root: $MAIN_ROOT"
      PROJECT_ROOT="$MAIN_ROOT"
    fi
  fi
fi

注意逃生阀:设置环境变量 UNDERSTAND_NO_WORKTREE_REDIRECT=1 可让输出保留在 worktree 中,适合你明确知道会话产物无需长期保存的场景。

0.2 数据目录 $UA_DIR:新旧目录并存策略

所有 Understand-Anything 产物都放在项目数据目录中。解析规则是一行:

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

即:若旧版 .understand-anything/ 目录已存在则继续沿用(老项目无需迁移),否则使用新版 .ua/。由于每个 Phase 可能运行在全新 shell 中,$UA_DIR 需要像 $PROJECT_ROOT 一样向后传递,必要时用上面这行重新解析。

0.3 插件根目录 $PLUGIN_ROOT 的多候选解析

SKILL.md 明确警告:不要假设插件根就是技能路径上两级父目录——在 ~/.agents/skills/understand-domain 这类安装中,它通常是指向真实插件检出的符号链接。正确的解析顺序是:优先使用运行时注入的 ${CLAUDE_PLUGIN_ROOT},然后依次尝试 ~/.understand-anything-plugin、由 ~/.agents/skills/understand-domain 真实路径上溯两级、由 ~/.copilot/skills/understand-domain 上溯两级,最后回退到 codex/opencode/pi 等常见的克隆安装路径。每个候选必须同时存在 package.jsonpnpm-workspace.yaml 才被采纳,全部落空则报错退出并打印所有已检查路径。$PLUGIN_ROOT 在后续 Phase 中用于定位 agent 定义文件。

Phase 1:检测既有图谱与新鲜度检查

流程是:

  1. 检查 $UA_DIR/knowledge-graph.json 是否存在;
  2. 若存在且未传 --full,先做新鲜度预检再决定从图谱派生:
    • 从图谱元数据读取 project.gitCommitHash(记为 GRAPH_COMMIT_RAW),先 git rev-parse --verify --end-of-options "${GRAPH_COMMIT_RAW}^{commit}" 将其解析为真实提交,再与 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 -- .
      
    • -- . 路径限定是必须的:只触及 monorepo 中兄弟项目提交的 commit 不应让本项目的图谱变旧;仅哈希不一致、而项目内 diff 为空,不算过期。

    • 所有命令输出中都要忽略数据目录本身(.ua/.understand-anything/),因为里面是生成产物而非源码漂移。

    • 若检测到项目文件变更,提示“领域提取可能遗漏这些变更”,并建议先运行 /understand 刷新知识图谱。

    • 提交 diff 仅在 GRAPH_COMMIT_RAW 成功解析时才执行;图谱 commit 或 Git 元数据缺失/非法时,只做尽力而为的警告并继续,不阻塞流程。

  3. 预检通过后进入 Phase 3(从图谱派生);否则进入 Phase 2(轻量扫描)。使用 --full 时跳过预检,因为此时命令会执行全新扫描而不是消费既有图谱。

Phase 2:轻量扫描(路径 1)与 extract-domain-context.py 全解析

SKILL.md 对这一步的定位非常明确:预处理脚本不产生领域图谱,它产出的是原材料——文件树、入口点、导出/导入——让 domain-analyzer agent 把宝贵的工具调用次数花在真正的领域分析上,而不是花几十次调用探索代码库。设计文档将其概括为:便宜的 Python 预处理 → 昂贵的 LLM 拿到干净的小输入 → 更低成本、更好结果。

调用方式为:

python ./extract-domain-context.py "$PROJECT_ROOT"

产物是 $UA_DIR/intermediate/domain-context.json,随后在 Phase 4 作为上下文使用。下面结合 extract-domain-context.py 的源码逐块解析。

2.1 限额常量:为 Agent 上下文预算设计的扫描器

脚本头部的一组常量(extract-domain-context.py#L29-L37)定义了扫描的规模上限:

常量 作用
MAX_FILE_TREE_DEPTH 6 文件树递归深度上限
MAX_FILES_PER_DIR 50 单目录最多收录文件数
MAX_FILES_TOTAL 5000 全局源文件上限
MAX_SAMPLED_FILES 40 参与签名提取的抽样文件数
MAX_LINES_PER_FILE 80 每文件读取行数上限
MAX_ENTRY_POINTS 200 入口点数量上限
MAX_OUTPUT_BYTES 512 KB 输出体积上限,保证不超出 agent 上下文限制

SOURCE_EXTENSIONS 覆盖 25+ 种语言扩展名(TS/JS、Python、Go、Rust、Java/Kotlin/Scala、Ruby、C#、PHP、Swift、C/C++、Elixir、Haskell、Lua、R 等),SKIP_DIRS 硬编码跳过了 node_modules.gitdistbuild__pycache__.nexttargetPods 等构建与依赖目录,同时也跳过了 .ua.understand-anything 数据目录。

2.2 文件树扫描与 .gitignore 感知

scan_file_tree() 从项目根做深度优先遍历,按“目录优先、名称字母序”排序,跳过符号链接(防死循环)、SKIP_DIRS 命中项与 .gitignore 命中项。.gitignore 的解析(parse_gitignore())是一个简化版的 glob 到正则转换:**/ 映射为 (.*/)?* 映射为 [^/]*? 映射为 [^/],以 / 结尾的模式补 (/|$)。无效模式会向 stderr 打印警告并跳过,不会中断扫描。

2.3 入口点检测:静态模式匹配而非 AST

detect_entry_points() 用一组预编译正则(extract-domain-context.py#L77-L121)对每个源文件全文做匹配,命中后记录行号与“签名 + 前 5 行”的代码片段(截断到 300 字符)。模式按业务语义分为五类:

类型 覆盖的模式
http Express/Koa 路由(app.get/post/put/patch/delete/all/use)、装饰器路由(Flask/FastAPI/NestJS 的 @route/@get/@api_view 及 Spring 的 @RequestMapping/@GetMapping)、Next.js/Remix 路由处理器(export async function GET/POST/...)、GraphQL resolver(@Query/@Mutation/...)、gRPC service(.proto 中的 service Xxx {
cli Commander 风格 .command('name')、argparse add_parser('name')
event .on('event-name') 监听器、@EventHandler/@Subscribe/@Listener/@on_event 装饰器
cron @Cron/@Schedule/@Scheduled/crontab('...')
manual 泛化导出的处理器:export (async) function handleX/processX/onX

扫描会自动跳过测试文件(.test..spec.__tests___test.pytest_*.py)与脚本自身,总量到达 MAX_ENTRY_POINTS(200)即停止。每个入口点记录 filelinetypedescriptionmatch(截断至 120 字符)与 snippet 六个字段。

2.4 文件签名:按业务关键词优先抽样

extract_file_signatures() 并不平等对待所有文件——它先用一组业务关键词(controllerservicehandlerrouterrouteapimodelentityrepositoryusecasecommandqueryeventsubscriberlistenermiddlewareguardinterceptorresolverworkflowflowprocesspipelinejobtask)对路径打分,得分高的文件优先,取前 40 个(MAX_SAMPLED_FILES)读取前 80 行,提取:

  • exports:JS/TS 的 export (default)? (async)? function|class|const|... 名称;Python 回退到顶层 def/class
  • importsimport ... from '...'from ... import 两类模式,各截前 20 条;
  • preview:文件头部 500 字符。

这套“关键词优先级”设计从源码结构看体现了明确的假设:业务领域知识最可能存在于控制器、服务、处理器这类命名惯例中。

2.5 元数据抽取与渐进式截断

extract_metadata() 会读取 METADATA_FILES 清单(package.jsonCargo.tomlgo.modpyproject.tomlpom.xmlbuild.gradleGemfiledocker-compose.yml、各类 README 等)。对 package.json 做结构化解析(只保留 name、description、scripts 键、依赖名列表),README 截取前 2000 字符,TOML/JSON/YAML/XML/Gradle 文件截取前 1000 字符。

最终输出前,_truncate_to_fit() 保证整个 JSON 不超过 512 KB,采用四级渐进裁剪(extract-domain-context.py#L350-L380):

  1. 文件树截断到前 200 条;
  2. 文件签名中的 preview 截断到 200 字符;
  3. 入口点中的 snippet 截断到 100 字符;
  4. 签名数减到 20 条、入口点数减到 100 条。

这种“逐层降级而不是丢弃”的策略确保了任何规模的项目都能得到一份可被 LLM 一次消化的上下文文件。domain-context.json 的完整结构为:

{
  "projectRoot": "...",
  "fileCount": 123,
  "fileTree": ["src/..."],
  "entryPoints": [{ "file": "...", "line": 42, "type": "http", "description": "...", "match": "...", "snippet": "..." }],
  "fileSignatures": [{ "file": "...", "exports": [], "imports": [], "lines": 0, "preview": "..." }],
  "metadata": { "package.json": { "name": "...", "description": "...", "scripts": [], "dependencies": [], "devDependencies": [] } }
}

Phase 3:从既有图谱派生(路径 2)

若 Phase 1 确认 knowledge-graph.json 新鲜,则直接读取该文件并将其格式化为结构化上下文,包含:

  • 全部节点(类型、名称、摘要、标签);
  • 全部边(类型,尤其是 callsimportscontains);
  • 全部分层(layers)及其描述;
  • Tour 步骤(如有)。

这份上下文直接交给 domain-analyzer,不需要读取任何源文件。这是该路径成本远低于路径 1 的原因:/understand 产出的节点摘要已经完成了“代码说了什么”的第一遍翻译,领域分析只是在其上做第二次抽象。

Phase 4:领域分析与三级层次模型

技能会读取 $PLUGIN_ROOT/agents/domain-analyzer.md(即 domain-analyzer.md)的 agent 提示词,派发一个子 agent,把 Phase 2 或 Phase 3 的上下文一并注入。该 agent 是“业务领域分析专家”,其核心产出遵循三级层次

  1. Business Domain——高层业务领域(如“Order Management”“User Authentication”“Payment Processing”);
  2. Business Flow——领域内的具体流程(如“Create Order”“Process Refund”);
  3. Business Step——流程中的单个动作(如“Validate input”“Check inventory”)。

agent 的提示词规定了严格的输出 schema(versionproject 元数据、nodesedges、以及有意留空的 layerstour——Dashboard 用独立的领域视图渲染,不走 layers/tour),关键约束值得逐条对照:

  • flow_step 边的 weight 编码步骤顺序:N 个步骤时,第 i 步 weight 为 round(i/N, 1),5 步即 0.1/0.2/0.3/0.4/0.5;核心要求是 weight 单调递增且全部落在 0.0–1.0 闭区间内;
  • 每个 flow 必须通过 contains_flow 边挂到某个 domain,每个 step 必须通过 flow_step 边挂到某个 flow;
  • cross_domain 边描述领域间交互,可用 description 字段解释交互内容;
  • step 节点的 filePath 必须是相对项目根的路径,无法确定时就省略 filePathlineRange
  • 必须使用代码中真实的业务术语,不得发明代码中不存在的流程;
  • 规模建议:2–6 个 domain、每 domain 2–5 个 flow、每 flow 3–8 个 step,小项目可以更少;
  • 节点 ID 前缀后必须 kebab-case(domain:order-management 而非 domain:OrderManagement),所有节点非空 summary、至少一个 tag,complexitysimple|moderate|complex 之一,禁止重复 ID 与自环边。

节点结构示例(引自 agent 提示词):

{
  "id": "domain:order-management",
  "type": "domain",
  "name": "Order Management",
  "summary": "<2-3 sentences about what this domain handles>",
  "tags": ["<relevant-tags>"],
  "complexity": "simple|moderate|complex",
  "domainMeta": {
    "entities": ["<key domain objects>"],
    "businessRules": ["<important constraints/invariants>"],
    "crossDomainInteractions": ["<how this domain interacts with others>"]
  }
}

flow 节点的 domainMeta 则记录触发方式:entryPoint(如 POST /api/orders)与 entryTypehttp|cli|event|cron|manual)——注意这与 Phase 2 预处理器检测出的入口点类型枚举完全对应,两条路径产出的字段语义是一致的。

agent 将结果写入 $UA_DIR/intermediate/domain-analysis.json,文本回复只允许一段简短摘要(domain/flow/step 数量与关键领域名),不得把完整 JSON 贴进对话。

Phase 5:校验与保存——错误容忍的落盘策略

SKILL.md 规定:读取分析输出 → 用标准图谱校验流水线校验(schema 已支持 domain/flow/step 节点类型)→ 校验失败时记录警告但保存有效部分(错误容忍)→ 保存到 $UA_DIR/domain-graph.json → 清理中间文件 intermediate/domain-analysis.jsonintermediate/domain-context.json

在核心包中,这套类型系统落在 schema.tsGraphNodeSchematype 枚举显式包含 "domain", "flow", "step" 三种节点类型(schema.ts#L420-L440),并带有可选的 domainMeta 字段;边类型枚举同样收录了 contains_flowflow_stepcross_domain,且 weight 被约束为 0–1 闭区间——这与 domain-analyzer 提示词中的手工约束是同一套规则的机器校验版本。schema 中还维护了一份别名归一化表(如 business_domain → domainhas_flow → contains_flow),意味着 LLM 输出的措辞变体在校验时会被自动纠正,这是 Phase 5 “保存有效部分”能够成立的前提。

落盘逻辑见 persistence/index.ts#L165-L197saveDomainGraph() 写入前会先对 filePath 做净化(sanitiseFilePaths)再序列化;loadDomainGraph() 默认走 validateGraph 校验,校验失败抛出携带 fatal 原因的错误,也支持 validate: false 的宽松读取。从源码结构看,领域图谱与结构图谱共用同一套 KnowledgeGraph 类型与校验器,这正是设计文档中“Separate File, Shared Schema”(方案 C)的实现:两份文件各自独立有效,搜索、校验、过滤对两者通用,同时不污染结构图谱。

Phase 6:启动 Dashboard 的领域视图

技能的最后一步是自动触发 /understand-dashboard(见 understand-dashboard/SKILL.md)来可视化领域图谱。Dashboard 会检测项目数据目录下的 domain-graph.json 并默认切换到领域视图——横向流程图形式,domain 为簇、flow 为泳道、step 按 flow_step weight 的顺序从左到右排列,cross_domain 边则展示领域间交互。领域视图组件的实现在 DomainGraphView.tsx。启动方式上,dashboard 技能优先走免安装的预构建 viewer,失败则回退到 pnpm install + Vite 开发服务器,最终都会打印带 ?token= 参数的访问 URL(token 用于通过访问门禁)。

关键产物与文件速查

产物/文件 位置 说明
技能定义 understand-anything-plugin/skills/understand-domain/SKILL.md 六阶段工作流的权威描述
预处理器 understand-anything-plugin/skills/understand-domain/extract-domain-context.py 轻量扫描器,产出原材料 JSON
分析 agent understand-anything-plugin/agents/domain-analyzer.md 三级层次模型、输出 schema 与约束规则
领域上下文 $UA_DIR/intermediate/domain-context.json Phase 2 产物,Phase 5 后清理
领域分析结果 $UA_DIR/intermediate/domain-analysis.json Phase 4 产物,Phase 5 后清理
最终图谱 $UA_DIR/domain-graph.json 与知识图谱同 schema,独立有效
类型与校验 understand-anything-plugin/packages/core/src/schema.ts domain/flow/step 节点与四类边
持久化 understand-anything-plugin/packages/core/src/persistence/index.ts saveDomainGraph / loadDomainGraph
设计文档 docs/superpowers/specs/2026-04-01-business-domain-knowledge-design.md 背景动机、双路径架构与 token 成本估算

小结

/understand-domain 的工程质量体现在三处克制:其一,输入侧用 512 KB 硬上限、四级渐进截断与业务关键词优先级抽样,确保喂给 LLM 的上下文永远可控;其二,分析侧把“扫描”与“理解”分离——便宜的静态预处理负责找事实(入口点、文件签名),昂贵的 LLM 只做业务抽象,且提示词以单调 weight、kebab-case ID、不虚构流程等硬约束压缩幻觉空间;其三,输出侧复用知识图谱的 schema 与校验器,让领域图谱天然享受搜索、过滤与 Dashboard 渲染的全部基础设施,同时以独立文件保证结构图谱零污染。对于已经跑过 /understand 的项目,--full 之外的默认路径几乎不产生额外文件读取成本,这使得“先全量理解、后按需补领域视图”成为一条低摩擦的使用路线。

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