Understand-Anything 业务领域知识提取全解析:domain/flow/step 图谱扩展与 Dashboard 领域视图实现
本文以 Understand-Anything 仓库中「Business Domain Knowledge Extraction」实施计划为骨架,完整还原该功能从核心类型扩展、Zod 校验、独立持久化、节点 ID 规范化,到 Dashboard 领域视图组件、/understand-domain 技能管线的全部实现细节。读完本文,你将掌握:如何在既有 KnowledgeGraph 体系上扩展三种领域节点类型与三种领域边类型、domain-graph.json 的读写与隐私脱敏机制,以及 dashboard 如何在结构视图与业务领域流图之间通过 pill 切换。
一、功能定位与整体架构
该实施计划(见 2026-04-01-business-domain-knowledge-impl.md,设计规格见 2026-04-01-business-domain-knowledge-design.md)要解决的核心问题是:让 Understand-Anything 不仅能画出"代码长什么样"(结构图谱),还能画出"业务是怎么跑的"(领域流图)。其目标、架构与技术栈在计划开头即明确定义:
- Goal:新增
/understand-domain技能,从代码库中提取业务领域知识(domains、business flows、process steps),并在 dashboard 中渲染为交互式横向流图; - Architecture:独立的
domain-graph.json文件,使用扩展后的KnowledgeGraphschema(3 种新节点类型、3 种新边类型、可选domainMeta字段);存在两条分析路径——轻量扫描(无既有图谱时)或从既有图谱派生(有图谱时,更廉价);dashboard 在有领域图时默认展示领域视图,并可用 pill 切回结构视图; - Tech Stack:TypeScript、Zod、React Flow(横向 LR 布局)、Zustand、Vitest、web-tree-sitter。
整个实施分为 15 个任务,按依赖顺序推进:先做 core 包的类型与 schema(Task 1),再做持久化(Task 2)与 ID 规范化(Task 3),然后构建验证(Task 4),接着是 dashboard 侧的状态(Task 5)、视图切换(Task 6)、三类节点组件(Task 7–8)、领域视图(Task 9)、侧栏(Task 10)、分类映射(Task 11),最后是 agent 与技能定义(Task 12–13)、dashboard 文件服务(Task 14)和集成验证(Task 15)。下文按这一脉络逐层展开,并对照当前仓库源码说明每个环节的落地形态。
二、核心类型扩展:3 节点类型、3 边类型与 DomainMeta
2.1 NodeType 与 EdgeType 联合类型的扩展
计划 Task 1 要求在 types.ts 中把 domain、flow、step 加入 NodeType 联合类型,并新增三种领域边类型。计划中的目标形态为:
// Node types (16 total: 5 code + 8 non-code + 3 domain)
export type NodeType =
| "file" | "function" | "class" | "module" | "concept"
| "config" | "document" | "service" | "table" | "endpoint"
| "pipeline" | "schema" | "resource"
| "domain" | "flow" | "step";
// Edge types (30 total in 7 categories)
export type EdgeType =
| "imports" | "exports" | "contains" | "inherits" | "implements" // Structural
| "calls" | "subscribes" | "publishes" | "middleware" // Behavioral
| "reads_from" | "writes_to" | "transforms" | "validates" // Data flow
| "depends_on" | "tested_by" | "configures" // Dependencies
| "related" | "similar_to" // Semantic
| "deploys" | "serves" | "provisions" | "triggers" // Infrastructure
| "migrates" | "documents" | "routes" | "defines_schema" // Schema/Data
| "contains_flow" | "flow_step" | "cross_domain"; // Domain
对照当前源码,这三项扩展已完整落地。从源码结构看,此后项目又先后引入了 knowledge 图谱(article/entity/topic/claim/source)与 Figma 设计图谱(page/screen/component 等)节点类型,NodeType 现已扩展为 27 种(5 code + 8 non-code + 3 domain + 5 knowledge + 6 design),EdgeType 扩展到 38 种 9 大类——领域三节点与领域三边作为其中独立的一类被稳定保留,可见见 types.ts。
三种领域边类型各自的语义在计划中通过 dashboard 渲染逻辑得到强化:
| 边类型 | 方向语义 | 在流图中的表现 |
|---|---|---|
contains_flow |
domain → flow | 领域包含流程,weight 固定 1.0,同时被用于统计每个 domain 的 flow 数量 |
flow_step |
flow → step | 流程包含步骤,weight 编码步骤顺序(第 1 步 0.1、第 2 步 0.2……),dashboard 用它计算步骤编号 |
cross_domain |
domain → domain | 领域间交互,带 description 字段说明交互内容,在概览图中渲染为动画虚线 |
2.2 DomainMeta 可选元数据
计划要求在 GraphNode 之后新增 DomainMeta 接口,为三类领域节点提供可选的结构化元数据:
// Optional domain metadata for domain/flow/step nodes
export interface DomainMeta {
// For domain nodes
entities?: string[];
businessRules?: string[];
crossDomainInteractions?: string[];
// For flow nodes
entryPoint?: string;
entryType?: "http" | "cli" | "event" | "cron" | "manual";
}
字段与节点类型的对应关系:domain 节点携带 entities(核心实体)、businessRules(业务规则/不变量)、crossDomainInteractions(与外部领域的交互方式);flow 节点携带 entryPoint(触发器,如 POST /api/orders)与 entryType(http/cli/event/cron/manual 五选一)。当前 types.ts 中该接口与计划完全一致,GraphNode 以 domainMeta?: DomainMeta 可选字段挂载(见 types.ts)。
2.3 Zod 校验层:枚举、别名与 .passthrough()
计划 Task 1 Step 4 对 schema.ts 的改造分四步,全部可以在当前源码中逐条对应:
(1)扩展 EdgeTypeSchema 枚举,追加三种领域边:
export const EdgeTypeSchema = z.enum([
"imports", "exports", "contains", "inherits", "implements",
"calls", "subscribes", "publishes", "middleware",
"reads_from", "writes_to", "transforms", "validates",
"depends_on", "tested_by", "configures",
"related", "similar_to",
"deploys", "serves", "provisions", "triggers",
"migrates", "documents", "routes", "defines_schema",
"contains_flow", "flow_step", "cross_domain",
]);
当前 schema.ts 中该枚举已包含这三个值(另含 Knowledge/Design 类边)。
(2)节点类型别名表。计划提出 business_domain: "domain"、workflow: "flow"、action: "step" 等别名。对照当前 schema.ts 的落地版本:
// Domain aliases — "process" intentionally excluded (ambiguous with OS/Node.js process)
business_domain: "domain",
business_flow: "flow",
business_process: "flow",
task: "step",
business_step: "step",
从源码注释可以看出两处有意的调整:process 因与操作系统/Node.js 进程语义混淆被刻意排除;workflow/action 这类歧义更大的通用词也未纳入。边别名则保留了计划中的三项(见 schema.ts):
// Domain aliases
has_flow: "contains_flow",
next_step: "flow_step",
interacts_with: "cross_domain",
值得注意的是,计划中 implemented_by: "implements" 这一条没有被采纳——当前源码注释明确说明 implemented_by 会反转边的 source/target 方向,LLM 应直接使用方向正确的 implements(见 schema.ts)。
(3)GraphNodeSchema 扩展节点枚举并启用 .passthrough():
export const GraphNodeSchema = z.object({
id: z.string(),
type: z.enum([
"file", "function", "class", "module", "concept",
"config", "document", "service", "table", "endpoint",
"pipeline", "schema", "resource",
"domain", "flow", "step",
]),
name: z.string(),
filePath: z.string().optional(),
lineRange: z.tuple([z.number(), z.number()]).optional(),
summary: z.string(),
tags: z.array(z.string()),
complexity: z.enum(["simple", "moderate", "complex"]),
languageNotes: z.string().optional(),
}).passthrough();
计划中的关键设计意图是:.passthrough() 让 domainMeta 等额外字段无需在 Zod 中定义即可通过校验,保持 schema 简洁且前向兼容。当前 schema.ts 在保留 .passthrough() 的基础上更进一步,为 domainMeta 显式定义了 DomainMetaSchema(字段与 DomainMeta 接口一一对应,同样 .passthrough()),使其获得完整的类型约束。
(4)失败驱动的测试验证。计划为 Task 1 编写的失败测试现已落地为 domain-types.test.ts,覆盖七类断言:domain/flow/step 节点图整体可校验、contains_flow/flow_step/cross_domain 边类型可校验、节点类型别名归一化(business_domain→domain 等)、边类型别名归一化(has_flow→contains_flow、next_step→flow_step)、domainMeta 经校验后完整保留。测试使用的领域图样例本身就是理解 schema 的最佳示例——3 个节点(domain:order-management、flow:create-order、step:create-order:validate)与 2 条边,其中 flow 节点带 domainMeta: { entryPoint: "POST /api/orders", entryType: "http" },step 节点带 filePath: "src/validators/order.ts" 与 lineRange: [10, 30]。运行方式(计划 Step 2/5):
cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/__tests__/domain-types.test.ts
三、独立持久化:domain-graph.json 的读写与路径脱敏
计划 Task 2 在 persistence/index.ts 中新增两个函数,与结构图谱的 saveGraph/loadGraph 平行但独立成文件:
const DOMAIN_GRAPH_FILE = "domain-graph.json";
export function saveDomainGraph(projectRoot: string, graph: KnowledgeGraph): void {
const dir = ensureDir(projectRoot);
const sanitised = sanitiseFilePaths(graph, projectRoot);
writeFileSync(
join(dir, DOMAIN_GRAPH_FILE),
JSON.stringify(sanitised, null, 2),
"utf-8",
);
}
export function loadDomainGraph(
projectRoot: string,
options?: { validate?: boolean },
): KnowledgeGraph | null {
const filePath = join(projectRoot, UA_DIR, DOMAIN_GRAPH_FILE);
if (!existsSync(filePath)) return null;
const data = JSON.parse(readFileSync(filePath, "utf-8"));
if (options?.validate !== false) {
const result = validateGraph(data);
if (!result.success) {
throw new Error(
`Invalid domain graph: ${result.fatal ?? "unknown error"}`,
);
}
return result.data as KnowledgeGraph;
}
return data as KnowledgeGraph;
}
当前实现(persistence/index.ts)与计划一致,并有两点值得展开的细节:
- 写盘前强制脱敏。
saveDomainGraph与saveGraph一样先经过sanitiseFilePaths:LLM 产出的绝对路径(如/Users/alice/company/src/auth.ts)会被转换为相对projectRoot的相对路径;项目外部的绝对路径只保留文件名(见 persistence/index.ts 的三分支处理)。这意味着domain-graph.json不会泄露开发者主目录与公司目录结构——而该文件恰恰会被 dashboard 服务器直接对外服务,脱敏是安全闭环的一环。 - 数据目录的兼容策略。当前源码中数据目录由
resolveUaDirName决定:已存在.understand-anything/的旧项目继续沿用该目录,新项目使用.ua/(见 persistence/index.ts)。计划文本中写的是.understand-anything/domain-graph.json,实际运行时两者都可能出现,loadDomainGraph通过resolveUaDir自动适配。
导出层面,计划指出 index.ts 中的 export * from "./persistence/index.js" 通配导出会自动带出新函数,无需改动。持久化测试 domain-persistence.test.ts 验证了三个要点:存取往返一致、文件不存在时返回 null、以及写入的是 domain-graph.json 而非 knowledge-graph.json——最后一条正是"独立成文件、互不干扰"架构决策的回归保障。
四、节点 ID 规范化:domain/flow/step 前缀与 step 的去碰撞重建
计划 Task 3 要求在 normalize-graph.ts 的 VALID_PREFIXES 与 TYPE_TO_PREFIX 中登记三个新前缀:
const VALID_PREFIXES = new Set([
"file", "func", "class", "module", "concept",
"config", "document", "service", "table", "endpoint",
"pipeline", "schema", "resource",
"domain", "flow", "step",
]);
const TYPE_TO_PREFIX: Record<string, string> = {
// ...
domain: "domain",
flow: "flow",
step: "step",
};
当前源码(normalize-graph.ts)与计划完全一致。计划对应的测试 domain-normalize.test.ts 断言了四个场景:domain:order-management、flow:create-order 原样通过(幂等);带 filePath 的 step 归一化为 step:src/validators/order.ts:validate;无 filePath 的 step:validate 保持不变。
从源码结构看,step 前缀的规范化是三类领域中唯一有"特殊逻辑"的。normalizeNodeId 对 step 类型做了重建处理:当 step 携带 filePath 时,ID 会重构为 step:<flowSlug>:<filePath>:<stepSlug> 形式(见 normalize-graph.ts),其中 flowSlug 由 normalizeBatchOutput 扫描 flow_step 边反查得出(见 normalize-graph.ts)。这样设计的目的是避免两个不同 flow 在同一文件中存在同名 step 时发生 ID 碰撞——flow 判别段让 ID 全局唯一。这一点也解释了 agent 输出 schema 中 step ID 约定为 step:<flow-name>:<step-name> 的三层结构。
五、Dashboard 状态层:ViewMode、领域图状态与视图切换
5.1 Store 扩展
计划 Task 5 在 store.ts 中引入视图模式与领域状态。计划的目标形态:
export type ViewMode = "structural" | "domain";
// DashboardStore 新增字段
// Domain view
viewMode: ViewMode;
domainGraph: KnowledgeGraph | null;
activeDomainId: string | null;
setDomainGraph: (graph: KnowledgeGraph) => void;
setViewMode: (mode: ViewMode) => void;
navigateToDomain: (domainId: string) => void;
当前实现中 ViewMode 已扩展为 "structural" | "domain" | "knowledge" 三值(见 store.ts),核心的 viewMode/domainGraph/activeDomainId 状态与 setViewMode/navigateToDomain 动作与计划一致。setViewMode 在切换时会重置 selectedNodeId、focusNodeId、代码查看器等瞬态状态,避免跨视图选中态残留。
边分类映射也按计划落位:EDGE_CATEGORY_MAP 新增 domain 类别,DOMAIN_EDGE_TYPES 导出常量与之绑定(见 store.ts):
export const EDGE_CATEGORY_MAP: Record<EdgeCategory, string[]> = {
// ...
domain: ["contains_flow", "flow_step", "cross_domain"],
};
export const DOMAIN_EDGE_TYPES = EDGE_CATEGORY_MAP.domain;
5.2 领域图加载与 pill 切换
计划 Task 6 在 App.tsx 中做两件事。第一,加载 domain-graph.json(计划 Step 1 的目标代码):
useEffect(() => {
fetch(tokenUrl("/domain-graph.json", accessToken))
.then((res) => {
if (!res.ok) return null;
return res.json();
})
.then((data: unknown) => {
if (!data) return;
const result = validateGraph(data);
if (result.success && result.data) {
setDomainGraph(result.data);
}
})
.catch(() => {
// Silently ignore — domain graph is optional
});
}, [setDomainGraph]);
当前实现(App.tsx)保持同样的"静默容错"策略:领域图是可选产物,404 或解析失败都不影响结构视图。
第二,在 header 中渲染 pill 切换器,仅当两张图都存在时才显示(计划 Step 2 的目标代码):
{graph && domainGraph && (
<>
<div className="w-px h-5 bg-border-subtle" />
<div className="flex items-center bg-elevated rounded-lg p-0.5">
<button
onClick={() => setViewMode("domain")}
className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${
viewMode === "domain"
? "bg-accent/20 text-accent"
: "text-text-muted hover:text-text-secondary"
}`}
>
Domain
</button>
<button
onClick={() => setViewMode("structural")}
className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${
viewMode === "structural"
? "bg-accent/20 text-accent"
: "text-text-muted hover:text-text-secondary"
}`}
>
Structural
</button>
</div>
</>
)}
当前源码中该 pill 的显示条件为 graph && !isKnowledgeGraph && domainGraph(App.tsx),在计划的两图共存条件上叠加了"知识图谱模式下隐藏"的分支。文章开头引用的截图正是这一 UI:顶部 Domain/Structural 两态 pill,选中态以 bg-accent/20 text-accent 高亮。
5.3 主区域的条件渲染
计划 Task 9 Step 2 将 App.tsx 的图区替换为条件渲染:
{/* Graph area */}
<div className="flex-1 min-w-0 min-h-0 relative">
{viewMode === "domain" && domainGraph ? (
<DomainGraphView />
) : (
<GraphView />
)}
<div className="absolute top-3 right-3 text-sm text-text-muted/60 pointer-events-none select-none">
Press <kbd className="kbd">?</kbd> for keyboard shortcuts
</div>
</div>
当前 App.tsx 中该分支逻辑同样成立:viewMode === "domain" && domainGraph 时渲染 DomainGraphView,否则回落到结构视图 GraphView。
六、三类节点组件:DomainClusterNode、FlowNode、StepNode
计划 Task 7 与 Task 8 定义了领域视图的三档节点组件,均已落地为 DomainClusterNode.tsx、FlowNode.tsx、StepNode.tsx。
**DomainClusterNode(领域簇节点)**是概览层的核心组件,数据接口与渲染逻辑:
export interface DomainClusterData {
label: string;
summary: string;
entities?: string[];
flowCount: number;
businessRules?: string[];
domainId: string;
}
其交互设计有两层:单击 selectNode(data.domainId) 选中并在侧栏查看详情,双击 navigateToDomain(data.domainId) 下钻进入该领域的流程明细。视觉上以 serif 字体标题、实体 chips(最多 5 个,超出显示 +N)和 flow 计数呈现,min-w-[280px] max-w-[360px] 的宽度约束保证横向流图中卡片尺寸稳定。
**FlowNode(流程节点)**展示 entryPoint(等宽字体、accent 色调)、流程名、摘要与步骤计数,选中态切换 border/background。
**StepNode(步骤节点)**额外携带 order: number 字段——步骤顺序号在图上以等宽小数字展示,其来源是 flow_step 边的 weight(见下文 buildDomainDetail)。
三个组件都用 memo 包裹并暴露对应的 React Flow 节点类型别名(DomainClusterFlowNode、FlowFlowNode、StepFlowNode),供 DomainGraphView 的 nodeTypes 注册表引用:
const nodeTypes = {
"domain-cluster": DomainClusterNode,
"flow-node": FlowNode,
"step-node": StepNode,
};
七、DomainGraphView:横向领域流图的构建与布局
计划 Task 9 的 DomainGraphView.tsx 是该功能在 dashboard 侧的枢纽组件,其核心是两个纯函数构建器加一个带状态的记忆化外壳。
7.1 概览层 buildDomainOverview
function buildDomainOverview(graph: KnowledgeGraph): { nodes: Node[]; edges: Edge[] } {
const domainNodes = graph.nodes.filter((n) => n.type === "domain");
// ...
// Count flows per domain
const flowCountMap = new Map<string, number>();
for (const edge of graph.edges) {
if (edge.type === "contains_flow") {
flowCountMap.set(edge.source, (flowCountMap.get(edge.source) ?? 0) + 1);
}
}
// ... 映射为 "domain-cluster" 节点,尺寸 320x180 ...
const rfEdges: Edge[] = graph.edges
.filter((e) => e.type === "cross_domain")
.map((e) => ({
id: `${e.source}-${e.target}`,
source: e.source,
target: e.target,
label: e.description ?? "",
style: { stroke: "var(--color-accent)", strokeDasharray: "6 3", strokeWidth: 2 },
labelStyle: { fill: "var(--color-text-muted)", fontSize: 10 },
animated: true,
}));
return applyDagreLayout(rfNodes, rfEdges, "LR", DOMAIN_NODE_DIMENSIONS);
}
三个关键设计点:
- flow 计数复用
contains_flow边,而不是给 domain 节点另加字段——数据源保持单一; - cross_domain 边渲染为动画虚线(
strokeDasharray: "6 3"+animated: true),并在标签上显示description,这是截图中"领域 → 服务"横向虚线的来源; - 布局方向
"LR"(Left-to-Right) 实现横向流图语义:领域在左,跨领域交互向右展开。
7.2 明细层 buildDomainDetail
function buildDomainDetail(graph: KnowledgeGraph, domainId: string) {
// 该领域下的 flow 集合
const flowIds = new Set(
graph.edges
.filter((e) => e.type === "contains_flow" && e.source === domainId)
.map((e) => e.target),
);
const flowNodes = graph.nodes.filter((n) => flowIds.has(n.id));
const stepEdges = graph.edges.filter(
(e) => e.type === "flow_step" && flowIds.has(e.source),
);
const stepIds = new Set(stepEdges.map((e) => e.target));
const stepNodes = graph.nodes.filter((n) => stepIds.has(n.id));
// 步骤顺序:weight 即顺序
const stepOrderMap = new Map<string, number>();
for (const edge of stepEdges) {
stepOrderMap.set(edge.target, edge.weight);
}
// 每个 flow 的步骤数
const stepCountMap = new Map<string, number>();
for (const edge of stepEdges) {
stepCountMap.set(edge.source, (stepCountMap.get(edge.source) ?? 0) + 1);
}
// ... flow 节点 260x120,step 节点 200x90 ...
// step 的顺序号 = round(weight * 10)
return applyDagreLayout(rfNodes, rfEdges, "LR", dims);
}
这里 Math.round((stepOrderMap.get(node.id) ?? 0) * 10) 把 flow_step 边的 weight(0.1/0.2/0.3……)还原为整数步骤序号 1/2/3……,与 agent 输出规则"flow_step weight encodes order"首尾呼应。
7.3 从 dagre 到 ELK:布局引擎的演进
对照当前源码(DomainGraphView.tsx),可以推断项目在后续版本中将布局引擎从 dagre 切换为 ELK,但刻意保留了横向方向语义:
// DomainGraphView used dagre LR; preserve that direction with ELK.
const elkInput = nodesToElkInput(nodesArray, edgesArray, dims, {
"elk.direction": "RIGHT",
});
applyElkLayout(elkInput, { strict: import.meta.env.DEV })
.then(({ positioned, issues }) => { /* ... */ })
布局调用改为异步 useEffect(带 cancelled 竞态保护),布局问题通过 appendLayoutIssues 汇入全局告警横幅。节点尺寸常量(domain 320×180、flow 260×120、step 200×90)与计划一致。当没有领域图时,视图渲染引导文案:No domain graph available. Run /understand-domain to generate one.(DomainGraphView.tsx),形成"dashboard 提示 → 用户执行技能"的闭环。
八、NodeInfo 侧栏的领域感知
计划 Task 10 在 NodeInfo.tsx 中新增 DomainNodeDetails 组件(当前实现见 NodeInfo.tsx),为三类领域节点提供差异化详情面板:
- domain 节点:渲染
domainMeta.entities(chips)、businessRules(带 accent 符号的列表)、crossDomainInteractions(列表),以及由contains_flow边解析出的可点击 flow 列表(点击同时navigateToDomain+selectNode); - flow 节点:渲染
entryPoint(等宽字体)与按 weight 排序(.sort((a, b) => a.weight - b.weight))的步骤有序列表,每步可点击跳转; - step 节点:渲染
filePath与lineRange(文件路径:起始行-结束行格式),提供定位到实现的锚点。
主组件侧的接线逻辑是"活动图"概念:
const viewMode = useDashboardStore((s) => s.viewMode);
const domainGraph = useDashboardStore((s) => s.domainGraph);
const activeGraph = viewMode === "domain" && domainGraph ? domainGraph : graph;
const node = activeGraph?.nodes.find((n) => n.id === selectedNodeId);
当前源码(NodeInfo.tsx)与计划一致,并在详情区条件挂载:
{activeGraph && node && (node.type === "domain" || node.type === "flow" || node.type === "step") && (
<DomainNodeDetails node={node} graph={activeGraph} />
)}
计划 Task 11 还要求把领域类型纳入 GraphView.tsx 的 NODE_TYPE_TO_CATEGORY 映射(domain/flow/step 均归入 "code" 类别以便复用现有过滤器面板),该改动确保领域节点在结构视图侧的过滤体系中不丢失。
九、domain-analyzer Agent:三层业务语义与输出 Schema
计划 Task 12 定义了 domain-analyzer.md agent 提示词。它把代码库的分析对象从"符号"提升为三层业务语义:
- Business Domain——高层业务区域(如 "Order Management"、"User Authentication"、"Payment Processing");
- Business Flow——领域内的具体流程(如 "Create Order"、"Process Refund");
- Business Step——流程内的单个动作(如 "Validate input"、"Check inventory")。
agent 要求产出的 JSON 结构如下(计划原文 schema,与 core 包校验逻辑一一对应):
{
"version": "1.0.0",
"project": {
"name": "<project name>",
"languages": ["<detected languages>"],
"frameworks": ["<detected frameworks>"],
"description": "<project description focused on business purpose>",
"analyzedAt": "<ISO timestamp>",
"gitCommitHash": "<commit hash>"
},
"nodes": [
{
"id": "domain:<kebab-case-name>",
"type": "domain",
"name": "<Human Readable Domain Name>",
"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>"]
}
},
{
"id": "flow:<kebab-case-name>",
"type": "flow",
"name": "<Flow Name>",
"summary": "<what this flow accomplishes>",
"tags": ["<relevant-tags>"],
"complexity": "simple|moderate|complex",
"domainMeta": {
"entryPoint": "<trigger, e.g. POST /api/orders>",
"entryType": "http|cli|event|cron|manual"
}
},
{
"id": "step:<flow-name>:<step-name>",
"type": "step",
"name": "<Step Name>",
"summary": "<what this step does>",
"tags": ["<relevant-tags>"],
"complexity": "simple|moderate|complex",
"filePath": "<relative path to implementing file>",
"lineRange": [<start>, <end>]
}
],
"edges": [
{ "source": "domain:<name>", "target": "flow:<name>", "type": "contains_flow", "direction": "forward", "weight": 1.0 },
{ "source": "flow:<name>", "target": "step:<flow>:<step>", "type": "flow_step", "direction": "forward", "weight": 0.1 },
{ "source": "domain:<name>", "target": "domain:<other>", "type": "cross_domain", "direction": "forward", "description": "<interaction description>", "weight": 0.6 }
],
"layers": [],
"tour": []
}
计划附带的 7 条产出规则(agent 提示词原文)值得逐条理解:
- flow_step 的 weight 编码顺序:第一步 0.1、第二步 0.2……(dashboard 正是靠它排序与编号);
- 每个 flow 必须经
contains_flow连接到某个 domain; - 每个 step 必须经
flow_step连接到某个 flow; - 跨领域边用
cross_domain并描述交互方式; - step 节点的
filePath相对项目根; - 用代码中真实的业务术语,不泛化("Be specific, not generic");
- 不虚构代码中不存在的流程——只记录实际存在的内容。
最后一条约束是整个功能可信度的基石:领域图必须能从代码中找到落点,而不是 LLM 的"合理想象"。
十、/understand-domain 技能管线:两条路径、六个阶段
计划 Task 13 创建了 skills/understand-domain/SKILL.md,定义了完整的执行管线。技能 frontmatter 声明了 argument-hint: [--full],--full 用于在既有图谱存在时强制走全量扫描路径。
10.1 两条分析路径的选择
- Path 1(轻量扫描):无既有知识图谱时执行。运行随技能捆绑的预处理器 extract-domain-context.py:
python ./extract-domain-context.py "$PROJECT_ROOT"
产出 $UA_DIR/intermediate/domain-context.json,包含:文件树(遵守 .gitignore)、检测到的入口点(HTTP 路由、CLI 命令、事件处理器、cron 任务、导出 handler)、每个文件的签名(exports/imports)、每个入口点的代码片段(签名 + 前几行)。从设计意图看,这一步是廉价 Python 预处理喂给昂贵 LLM 的"小抄":把几十个文件探索工具调用压缩为一次脚本执行,让 domain-analyzer 专注于真正的领域推理。
- Path 2(从图谱派生):
.ua/knowledge-graph.json(或旧目录.understand-anything/knowledge-graph.json)已存在且未传--full时,直接把图谱格式化结构化为上下文——所有节点(类型/名称/摘要/标签)、所有边(尤其calls/imports/contains)、所有 layer 描述、tour 步骤——零文件扫描,代价最低。
10.2 六阶段流程
| 阶段 | 动作 | 产物 |
|---|---|---|
| Phase 1 | 检测既有图谱并做新鲜度预检 | 决定走 Path 1 或 Path 2 |
| Phase 2 | 轻量扫描(仅 Path 1) | $UA_DIR/intermediate/domain-context.json |
| Phase 3 | 图谱派生(仅 Path 2) | 结构化图谱上下文(不落盘) |
| Phase 4 | 派发 domain-analyzer 子代理 | $UA_DIR/intermediate/domain-analysis.json |
| Phase 5 | 标准校验管线验证、容错保存 | $UA_DIR/domain-graph.json(清理 intermediate 文件) |
| Phase 6 | 自动触发 /understand-dashboard |
dashboard 检测到 domain-graph.json 并默认展示领域视图 |
Phase 5 的"容错"策略值得强调:校验失败时记录警告但保存有效部分(error tolerance),与 core 包 validateGraph 的分级设计(auto-corrected / dropped / fatal,见 schema.ts)一脉相承——LLM 产出的图允许局部瑕疵,不允许整体报废。
10.3 当前实现相对计划的增强
对照当前 SKILL.md,实现阶段追加了两块计划中未展开的工程细节:
- Phase 0 的 worktree 重定向:若
PROJECT_ROOT位于 git worktree 内,通过比较git rev-parse --git-dir与git rev-parse --git-common-dir检测,并把输出重定向到主仓库根,防止 Claude Code 临时 worktree 会话结束连带销毁领域图(issue #133);可用环境变量UNDERSTAND_NO_WORKTREE_REDIRECT=1关闭; - 新鲜度预检:Path 2 派生前会比较图谱
project.gitCommitHash与当前HEAD之间的项目级 diff(git diff --name-only "$GRAPH_COMMIT" HEAD -- .等四条命令),若项目文件有变更则提示先运行/understand刷新;-- .路径限定确保 monorepo 中兄弟项目的提交不会误判本项目图谱过期。
十一、构建、测试与文件清单
计划 Task 4 与 Task 15 定义了统一的验证命令矩阵,全部基于 pnpm workspace 的 --filter 机制:
# core 包构建与测试
cd understand-anything-plugin && pnpm --filter @understand-anything/core build
cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run
# dashboard 构建
cd understand-anything-plugin && pnpm --filter @understand-anything/dashboard build
# 全仓 lint
cd understand-anything-plugin && pnpm lint
计划 Task 14 则确认了 dashboard 服务器侧无需特殊改动:服务器按目录整体服务数据文件,domain-graph.json 与 knowledge-graph.json、meta.json 并列自动可用;App.tsx 中的 fetch 已验证该假设(App.tsx 中甚至为 demo 模式准备了 VITE_DOMAIN_GRAPH_URL 注入点)。
最后按计划的文件总清单收尾,全部文件在当前仓库中均可查证:
新增文件
| 文件 | 职责 |
|---|---|
| domain-types.test.ts | 领域节点/边类型与别名归一化测试 |
| domain-persistence.test.ts | domain-graph.json 存取与隔离测试 |
| domain-normalize.test.ts | domain/flow/step ID 前缀规范化测试 |
| DomainClusterNode.tsx | 领域簇节点(概览层) |
| FlowNode.tsx | 流程节点(明细层) |
| StepNode.tsx | 步骤节点(明细层) |
| DomainGraphView.tsx | 领域视图总组件(overview/detail 双构建器) |
| domain-analyzer.md | 领域分析 agent 提示词 |
| understand-domain/SKILL.md | /understand-domain 技能定义 |
| extract-domain-context.py | Path 1 轻量扫描预处理器 |
修改文件
| 文件 | 改动 |
|---|---|
| types.ts | 3 节点类型、3 边类型、DomainMeta 接口 |
| schema.ts | Zod 枚举扩展 + 领域节点/边别名表 + .passthrough() |
| persistence/index.ts | saveDomainGraph/loadDomainGraph |
| normalize-graph.ts | domain/flow/step 前缀与 step 去碰撞重建 |
| store.ts | viewMode/domainGraph/activeDomainId 状态与 DOMAIN_EDGE_TYPES |
| App.tsx | 领域图加载、pill 切换、条件渲染 |
| NodeInfo.tsx | DomainNodeDetails 领域感知侧栏 |
| GraphView.tsx | NODE_TYPE_TO_CATEGORY 纳入领域类型 |
十二、小结
这套业务领域知识提取功能的工程价值在于三点:schema 级的前向兼容(.passthrough() + 别名归一化让 LLM 的非规范输出可收敛)、数据文件的职责隔离(domain-graph.json 独立于结构图谱,可单独缺失、独立刷新)以及渲染层对数据语义的忠实映射(weight 即顺序、contains_flow 计数、cross_domain 动画虚线、双击下钻)。对使用方而言,只需在安装了 Understand-Anything 插件的代码库中执行 /understand-domain(可选 --full),即可获得一张可交互的横向业务流图;对阅读源码者而言,上文按"类型 → 持久化 → 规范化 → dashboard → agent/技能"的调用链,提供了从 types.ts 到 SKILL.md 的完整定位索引。
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

