Understand-Anything domain-analyzer:把代码库提炼成"领域-流程-步骤"三层业务领域图的 Agent 设计解析
【免费下载链接】Understand-AnythingGraphs that teach > graphs that impress. Turn any code into an interactive knowledge graph you can explore, search, and ask questions about. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, and more.项目地址: https://gitcode.com/GitHub_Trending/un/Understand-Anything
domain-analyzer 是 Understand-Anything 插件中的业务领域分析专家 Agent:它接收"预处理领域上下文"或"已有知识图谱"二选一作为输入,产出一份三层结构(Business Domain → Business Flow → Business Step)的domain-graph.json,让仪表盘能够以交互式流程图的形式展示代码中的业务逻辑走向。读完本文,你将完整掌握该 Agent 的输入契约、输出 Schema 的每个字段含义、flow_step权重编码顺序的规则细节,以及在核心包中通过 Zod Schema 对领域图做校验与别名归一化的源码实现。
一、domain-analyzer 的角色与两种输入
在 Understand-Anything 的/understand-domain技能流水线中(见 understand-domain SKILL.md),真正"读懂业务"的是 domain-analyzer.md 定义的这个 Agent。它被调度技能(dispatching skill)以子 Agent 形式派发,收到的上下文恰好是以下两种之一:
- Option A — 预处理领域上下文(来自
domain-context.json):包含文件树、入口点、导出/导入关系和代码片段。这份 JSON 由轻量 Python 预处理脚本生成,适用于项目中还没有知识图谱的场景; - Option B — 已有知识图谱(来自
knowledge-graph.json):一份完整的结构化知识图谱,包含 nodes、edges、layers 和 tours。此时 Agent 直接从节点摘要、标签和关系推导领域知识,无需再读取任何源文件。
调度方会在 prompt 中明确告知适用哪种选项,并把上下文数据直接附在 prompt 中。Agent 的任务只有一件事:分析给定上下文,产出一份领域图 JSON 文件。
这种"双路径、同输出"的设计在 业务领域知识设计文档中有详细说明:路径 1(轻量扫描)的 token 成本约为完整/understand扫描的 10-20%,路径 2(从图谱推导)则几乎不需要读文件。
二、三层业务结构:Domain / Flow / Step
Agent 输出的领域图采用严格的三层层次结构:
| 层级 | 含义 | 文档示例 |
|---|---|---|
Business Domain(domain节点) | 高层业务区域 | "Order Management"、"User Authentication"、"Payment Processing" |
Business Flow(flow节点) | 领域内的具体流程 | "Create Order"、"Process Refund" |
Business Step(step节点) | 流程内的单个动作 | "Validate input"、"Check inventory" |
规模上文档给出了明确目标:2-6 个 domain、每个 domain 2-5 个 flow、每个 flow 3-8 个 step,小项目可以更少。同时强调两条纪律:"用代码中真实存在的业务术语,而不是泛泛的词汇"以及"不要发明代码中不存在的流程——只记录实际存在的东西"。
三、输出 Schema:完整 JSON 结构与字段说明
Agent 必须产出如下精确结构的 JSON(以下完整继承自 domain-analyzer.md 的 Output Schema 一节):
{ "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": [0, 0] } ], "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": [] }几个值得注意的细节:
- 节点 ID 前缀约定:
domain:、flow:、step:前缀之后必须使用kebab-case(如domain:order-management,而不是domain:OrderManagement);step 的 ID 形如step:<flow-name>:<step-name>,天然把步骤挂到所属流程上。 domainMeta按节点类型分工:domain 节点用entities/businessRules/crossDomainInteractions描述实体、业务规则与跨域交互;flow 节点用entryPoint/entryType标注触发方式(如POST /api/orders),entryType取值限定为http|cli|event|cron|manual。layers和tour有意留空:领域图由仪表盘单独视图渲染,不使用 layers 和 tours。- step 节点可携带代码定位:
filePath(相对项目根目录)+lineRange;若无法确定精确文件,则省略这两个字段而不是猜测。
flow_step 权重编码顺序:一条容易被忽视的关键规则
文档第一条规则是整个领域图"步骤有序"机制的核心:
flow_step 的 weight 编码顺序:使用 0-1 之间的小数权重。对于 N 个步骤:第一个 = 1/N 四舍五入到 1 位小数,第二个 = 2/N,依此类推。5 步示例:0.1, 0.2, 0.3, 0.4, 0.5;15 步示例:0.1, 0.1, 0.1, ...(步长为
round(1/N, 1),最小 0.1)。核心要求是权重单调递增且全部落在0.0 到 1.0 闭区间内。
这条规则的意义在仪表盘源码中得到印证:DomainGraphView.tsx 中,领域视图正是收集flow_step边,把edge.weight存入stepOrderMap,再按 weight 排序渲染"从左到右"的步骤链路——也就是说,Agent 输出的权重值直接决定流程图上步骤的先后顺序,权重乱序等于流程图乱序。
四、八条规则与硬性约束
domain-analyzer.md 在 Rules 一节给出 8 条分析规则:
- flow_step 权重编码顺序(如上节详述);
- 每个 flow 必须通过
contains_flow边连接到某个 domain; - 每个 step 必须通过
flow_step边连接到某个 flow——这保证了三层结构的连通性,任何节点都不允许"悬空"; - 跨域边(
cross_domain)描述 domain 之间的交互,可用可选的description字段解释交互内容; - step 节点的文件路径必须相对项目根目录;无法确定时省略
filePath与lineRange; - 具体而非泛化——使用代码中真实的业务术语;
- 不臆造代码中不存在的 flow;
- 规模适配:2-6 个 domain、每 domain 2-5 个 flow、每 flow 3-8 个 step。
Critical Constraints 一节进一步给出 6 条硬性约束,这些约束与后续校验环节一一对应:
- 所有节点 ID 前缀后必须是 kebab-case;
- 所有
weight值必须在 0.0 到 1.0 闭区间内; - 每个节点必须有非空
summary且至少一个 tag; complexity只能是simple/moderate/complex三者之一;- 不得创建重复节点 ID;
- 不得创建自引用边。
五、源码级佐证:核心包如何校验领域图
Agent 的约束并非"口头约定",而是在核心包的 Zod Schema 中强制执行。以下结论均可在仓库源码中直接确认。
5.1 节点类型与领域边类型是 Schema 的一等公民
packages/core/src/schema.ts 中,GraphNodeSchema的type枚举在常规代码节点(file、function、class……)之外显式包含"domain", "flow", "step"三个领域类型,并带有可选的domainMeta字段:
export const GraphNodeSchema = z.object({ id: z.string(), type: z.enum([ "file", "function", "class", "module", "concept", // ... "domain", "flow", "step", // ... ]), // ... summary: z.string(), tags: z.array(z.string()), complexity: z.enum(["simple", "moderate", "complex"]), domainMeta: DomainMetaSchema.optional(), }).passthrough();同文件中EdgeTypeSchema定义了 38 种边类型,其中领域专用的三种单列一类(schema.ts#L12):
"contains_flow", "flow_step", "cross_domain", // DomainGraphEdgeSchema则对 Agent 文档中"weight 必须在 0.0 到 1.0 之间"这条约束做了机器校验:weight: z.number().min(0).max(1)。
5.2 DomainMeta 的类型定义
packages/core/src/types.ts#L31-L38 给出了与 Agent 文档中domainMeta字段完全对应的 TypeScript 接口:
export interface DomainMeta { entities?: string[]; businessRules?: string[]; crossDomainInteractions?: string[]; entryPoint?: string; entryType?: "http" | "cli" | "event" | "cron" | "manual"; }entryType的五个枚举值与 Agent 文档中 flow 节点domainMeta.entryType的取值列表完全一致。
5.3 别名归一化:容忍 LLM 的"近义表达"
由于领域图由 LLM 生成,实际输出常用近义词而非规范类型名。schema.ts 维护了节点类型别名表,把 LLM 常见的变体归一化到规范类型:
// 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",边类型同样有别名表(schema.ts#L131-L133):has_flow → contains_flow、next_step → flow_step、interacts_with → cross_domain。值得注意的一个细节:注释明确说明故意排除process这个别名,因为它与操作系统/Node.js 的process概念有歧义。
5.4 测试用例:领域图的端到端校验示例
packages/core/src/tests/domain-types.test.ts 提供了一个最小但完整的领域图夹具,恰好就是 Agent 文档中"Order Management"示例的落地版本:domain:order-management→(contains_flow,weight 1.0)→flow:create-order(带domainMeta: { entryPoint: "POST /api/orders", entryType: "http" })→(flow_step,weight 0.1)→step:create-order:validate(带filePath: "src/validators/order.ts"与lineRange: [10, 30])。该测试文件验证了 5 类行为,均可直接运行复核:
- 含 domain/flow/step 节点与
contains_flow、flow_step边的图能通过validateGraph; - 追加
domain:logistics与带description的cross_domain边(weight 0.6)后仍合法; - 节点类型写成
business_domain/business_flow/business_step时被自动归一化为domain/flow/step; - 边类型写成
has_flow/next_step时被归一化为contains_flow/flow_step; - 校验过程中
domainMeta完整保留在输出节点上。
这组测试实质上就是对 Agent 输出契约的"验收标准":只要生成的domain-analysis.json满足文档中的 Schema 与约束(哪怕类型名写成别名),标准校验管线就能通过。
六、上下游流水线:上下文从哪来,结果到哪去
Agent 不是孤立运行的。结合 understand-domain SKILL.md 的六个阶段,完整链路如下:
Phase 0 — 确定 PROJECT_ROOT 与数据目录:输出落在项目的数据目录
$UA_DIR(新目录为.ua/,若项目已存在旧目录.understand-anything/则沿用旧目录,保证老项目无需迁移)。若当前在 git worktree 中,还会把输出重定向到主仓库根目录(worktree 会话结束数据会被销毁,issue #133);Phase 1 — 检测已有图谱:若
$UA_DIR/knowledge-graph.json存在且未传--full,先做新鲜度检查(对比图谱记录的gitCommitHash与git diff的项目范围变更),然后走"从图谱推导"路径;否则走轻量扫描;Phase 2 — 轻量扫描(Option A 的原料来源):运行 extract-domain-context.py 生成
$UA_DIR/intermediate/domain-context.json。设计文档将其定位为"cheat sheet":廉价 Python 预处理 → 昂贵 LLM 拿到干净的小输入 → 更低成本换更好结果。该脚本的上下文预算在源码顶部以常量固定(extract-domain-context.py#L31-L37):- 文件树最大深度 6 层、每目录最多 50 个文件、全局最多 5000 个文件;
- 签名采样最多 40 个文件、每文件最多 80 行;
- 入口点最多 200 个;
- 输出 JSON 硬上限 512 KB——
_truncate_to_fit()按"先砍文件树 → 再砍签名预览 → 再砍入口片段 → 最后减少签名/入口数量"的顺序渐进裁剪,保证不超出 Agent 上下文限制。
入口点检测覆盖五类触发模式(extract-domain-context.py#L77-L121),与 flow 节点
entryType的五个取值对应:Express/Koa/Flask/FastAPI 等 HTTP 路由、CLI 命令(.command、argparse 子命令)、事件监听(.on、@EventHandler等)、定时任务(@Cron/@Scheduled等),以及export function handleXxx/processXxx/onXxx这类通用导出处理器。文件签名提取还会按controller、service、handler、workflow、job等业务逻辑关键词给文件打分排序,优先采样最可能含业务逻辑的文件(extract-domain-context.py#L256-L272)。Phase 3 — 从图谱推导(Option B 的原料来源):直接读取
knowledge-graph.json,把全部节点(类型、名称、摘要、标签)、边(尤其calls/imports/contains)、层描述和 tour 步骤整理为结构化上下文,无需读任何源文件;Phase 4 — 领域分析:读取
$PLUGIN_ROOT/agents/domain-analyzer.md(即本文档),带着 Phase 2/3 的上下文派发子 Agent,Agent 把结果写入$UA_DIR/intermediate/domain-analysis.json;Phase 5 — 校验与保存:用标准图谱校验管线(Schema 已支持 domain/flow/step 类型)校验;校验失败时记录警告但保存有效部分(错误容忍策略);最终保存到
$UA_DIR/domain-graph.json,并清理中间文件domain-analysis.json与domain-context.json;Phase 6 — 启动仪表盘:自动触发
/understand-dashboard,仪表盘检测到domain-graph.json后默认展示领域视图。
七、结果写入与响应规范
domain-analyzer.md 的 Writing Results 一节对 Agent 的最终行为做了三点限定:
- JSON 必须写入项目数据目录(
.ua/,或存在时的旧目录.understand-anything/)下的intermediate/domain-analysis.json,使用 prompt 中给定的精确输出路径; - 项目根目录由 prompt 提供,Agent 不自行猜测;
- 文本响应只允许返回简短摘要:创建了哪些 domain、flow、step 的数量,以及关键领域名称;严禁在文本回复中贴出完整 JSON——这既避免污染会话上下文,也让 Phase 5 的校验环节有唯一可信的数据来源。
八、小结:一份"契约式"的 Agent 定义
从源码结构看,domain-analyzer.md 并不只是一个提示词,而是一份与工程管线严丝合缝的输出契约:它的节点/边类型对应 schema.ts 中的 Zod 枚举与别名表;它的 weight 区间约束对应z.number().min(0).max(1);它的flow_step权重排序约定被 DomainGraphView.tsx 直接消费为步骤渲染顺序;它的"Order Management"示例在 domain-types.test.ts 中有同名夹具做回归验证。对希望自定义或扩展该 Agent 的开发者来说,理解"文档约束 — Schema 校验 — 测试夹具 — 仪表盘消费"这条闭环,是保证领域图稳定生成的关键;对使用者而言,只需记住:保证上下文输入来自两条合法路径之一、让 Agent 只写intermediate/domain-analysis.json、由标准管线完成校验落盘,即可得到一份可交互探索的业务领域图。
【免费下载链接】Understand-AnythingGraphs that teach > graphs that impress. Turn any code into an interactive knowledge graph you can explore, search, and ask questions about. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, and more.项目地址: https://gitcode.com/GitHub_Trending/un/Understand-Anything
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考