Understand-Anything /understand-domain 技能深度解析:从代码库中自动提取业务领域知识并生成可交互流程图谱
在大型代码库中,结构依赖图能回答“谁调用了谁”,却回答不了“这段代码在做什么业务”。Understand-Anything 的 /understand-domain 技能正是为此设计:它通过一个 Skill 工作流加一个轻量 Python 预处理器,从代码库中提取业务领域(Domain)、业务流程(Flow)与流程步骤(Step)三级知识,生成独立的 domain-graph.json,并在 Dashboard 中以横向领域流程图呈现。读完本文,你可以完整掌握该技能的六阶段工作流、两条分析路径(轻量扫描 / 从既有知识图谱派生)、预处理器 extract-domain-context.py 的扫描策略与限额设计,以及领域图谱的 JSON 结构与校验持久化机制。
工作机制总览:两条路径,同一份输出
/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-dir 与 git 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.json 和 pnpm-workspace.yaml 才被采纳,全部落空则报错退出并打印所有已检查路径。$PLUGIN_ROOT 在后续 Phase 中用于定位 agent 定义文件。
Phase 1:检测既有图谱与新鲜度检查
流程是:
- 检查
$UA_DIR/knowledge-graph.json是否存在; - 若存在且未传
--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 元数据缺失/非法时,只做尽力而为的警告并继续,不阻塞流程。
-
- 预检通过后进入 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、.git、dist、build、__pycache__、.next、target、Pods 等构建与依赖目录,同时也跳过了 .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.py、test_*.py)与脚本自身,总量到达 MAX_ENTRY_POINTS(200)即停止。每个入口点记录 file、line、type、description、match(截断至 120 字符)与 snippet 六个字段。
2.4 文件签名:按业务关键词优先抽样
extract_file_signatures() 并不平等对待所有文件——它先用一组业务关键词(controller、service、handler、router、route、api、model、entity、repository、usecase、command、query、event、subscriber、listener、middleware、guard、interceptor、resolver、workflow、flow、process、pipeline、job、task)对路径打分,得分高的文件优先,取前 40 个(MAX_SAMPLED_FILES)读取前 80 行,提取:
- exports:JS/TS 的
export (default)? (async)? function|class|const|... 名称;Python 回退到顶层def/class; - imports:
import ... from '...'与from ... import两类模式,各截前 20 条; - preview:文件头部 500 字符。
这套“关键词优先级”设计从源码结构看体现了明确的假设:业务领域知识最可能存在于控制器、服务、处理器这类命名惯例中。
2.5 元数据抽取与渐进式截断
extract_metadata() 会读取 METADATA_FILES 清单(package.json、Cargo.toml、go.mod、pyproject.toml、pom.xml、build.gradle、Gemfile、docker-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):
- 文件树截断到前 200 条;
- 文件签名中的
preview截断到 200 字符; - 入口点中的
snippet截断到 100 字符; - 签名数减到 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 新鲜,则直接读取该文件并将其格式化为结构化上下文,包含:
- 全部节点(类型、名称、摘要、标签);
- 全部边(类型,尤其是
calls、imports、contains); - 全部分层(layers)及其描述;
- Tour 步骤(如有)。
这份上下文直接交给 domain-analyzer,不需要读取任何源文件。这是该路径成本远低于路径 1 的原因:/understand 产出的节点摘要已经完成了“代码说了什么”的第一遍翻译,领域分析只是在其上做第二次抽象。
Phase 4:领域分析与三级层次模型
技能会读取 $PLUGIN_ROOT/agents/domain-analyzer.md(即 domain-analyzer.md)的 agent 提示词,派发一个子 agent,把 Phase 2 或 Phase 3 的上下文一并注入。该 agent 是“业务领域分析专家”,其核心产出遵循三级层次:
- Business Domain——高层业务领域(如“Order Management”“User Authentication”“Payment Processing”);
- Business Flow——领域内的具体流程(如“Create Order”“Process Refund”);
- Business Step——流程中的单个动作(如“Validate input”“Check inventory”)。
agent 的提示词规定了严格的输出 schema(version、project 元数据、nodes、edges、以及有意留空的 layers 与 tour——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必须是相对项目根的路径,无法确定时就省略filePath与lineRange; - 必须使用代码中真实的业务术语,不得发明代码中不存在的流程;
- 规模建议:2–6 个 domain、每 domain 2–5 个 flow、每 flow 3–8 个 step,小项目可以更少;
- 节点 ID 前缀后必须 kebab-case(
domain:order-management而非domain:OrderManagement),所有节点非空summary、至少一个 tag,complexity取simple|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)与 entryType(http|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.json 与 intermediate/domain-context.json。
在核心包中,这套类型系统落在 schema.ts:GraphNodeSchema 的 type 枚举显式包含 "domain", "flow", "step" 三种节点类型(schema.ts#L420-L440),并带有可选的 domainMeta 字段;边类型枚举同样收录了 contains_flow、flow_step、cross_domain,且 weight 被约束为 0–1 闭区间——这与 domain-analyzer 提示词中的手工约束是同一套规则的机器校验版本。schema 中还维护了一份别名归一化表(如 business_domain → domain、has_flow → contains_flow),意味着 LLM 输出的措辞变体在校验时会被自动纠正,这是 Phase 5 “保存有效部分”能够成立的前提。
落盘逻辑见 persistence/index.ts#L165-L197:saveDomainGraph() 写入前会先对 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 之外的默认路径几乎不产生额外文件读取成本,这使得“先全量理解、后按需补领域视图”成为一条低摩擦的使用路线。
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 StartedRust0624
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
