画架构图这件事,看起来简单,做起来却非常消耗精力。模块少的时候还能手动拖几个框,一旦代码库到了几十个服务、上百张表、多层依赖关系,手工维护一张架构图几乎是不可能的任务。更麻烦的是,代码每天都在变,架构图画完就已经过时了。
最近在项目里尝试了一种新思路:把“读代码、理关系、画图”这套流程封装成一个 Skill,交给 AI Agent 自动执行。AI 自己遍历代码目录、分析模块依赖、识别分层关系,然后直接输出一张可用的架构图。整个过程不需要手动拖框,也不需要反复截图沟通。这篇文章就把这套方法的完整思路、Skill 工程结构、关键代码和常见坑点整理出来,希望能帮你摆脱“画图五分钟,维护两小时”的困境。
1. 背景:为什么“手动拖框”不是好方案
1.1 手动画架构图的三个痛点
先聊聊手动画架构图最常见的三个问题。
第一,信息滞后。架构图通常是阶段性产物,开发过程中模块拆分、接口调整、依赖变化非常频繁,图上的内容很快就会和真实代码脱节。团队里一旦换了维护人,图基本就没人敢动了。
第二,粒度难以把握。画太细,图会变成一张密密麻麻的蜘蛛网,没人愿意看;画太粗,又失去了架构图的价值。手动画图时,粒度的取舍完全依赖个人经验,不同人画出来的图风格差异极大。
第三,沟通成本高。架构评审、新人 onboarding、跨团队协作时,都需要有人花时间讲解“这张图是怎么来的”。如果图本身不能反映代码真实结构,讲解越多,误导越深。
1.2 为什么选择“让 AI 读代码出图”
既然手动画图有这么多问题,自然的想法是:能不能让工具自动生成架构图?
传统的静态分析工具确实可以生成调用关系图,但它们的输出通常非常机械,缺少分层、边界、模块职责这类高层视角。而架构图的核心价值,恰恰在于从高层理解系统,而不是罗列所有调用细节。
AI Agent 的优势在于,它可以结合代码内容做语义理解:知道哪个模块是入口、哪个模块是基础设施、哪些类属于领域层、哪些属于应用层。它不需要你手动指定关系,而是通过阅读代码自动判断。把这一套判断逻辑沉淀成 Skill 之后,AI 就能在每次代码变更后快速重新生成图,实现“架构图跟随代码更新”的效果。
1.3 适用场景
这种方案比较适合以下场景:
- 接手一个不熟悉的老项目,想快速了解模块结构。
- 微服务改造前,梳理现有服务之间的依赖关系。
- 代码评审前,生成当前分支的模块影响范围图。
- 团队文档建设,定期自动更新架构图。
如果你的项目非常小,只有几个文件,手动画图可能更快。但一旦代码库达到中大型规模,让 AI 边读代码边出图的效率优势就非常明显了。
2. 整体思路:AI 读代码出架构图的四个阶段
要让 AI 自动生成架构图,不能只写一句“帮我画架构图”就完事。清晰、可靠的流程应该拆成四个阶段,每个阶段都有明确的输入和输出。
2.1 阶段一:代码库探测与上下文收集
AI 需要先知道代码库里有哪些内容。这一阶段的核心是建立代码地图,包括:
- 项目目录结构。
- 各模块的入口文件。
- 配置文件、构建文件、部署文件。
- 每个目录的职责说明。
这一阶段不需要 AI 读完全部代码,而是通过目录结构和关键文件快速建立整体认知。
2.2 阶段二:模块与依赖建模
在了解代码地图后,AI 需要深入代码,提取模块之间的依赖关系。这里有两个层次:
- 静态依赖:import、require、依赖注入、接口调用。
- 语义依赖:某个模块在业务逻辑上依赖另一个模块提供的能力。
静态依赖可以通过脚本辅助提取,语义依赖则需要 AI 阅读代码后判断。实际操作中,通常是脚本提供候选依赖列表,AI 负责筛选和归类。
2.3 阶段三:分层与边界判断
架构图不能只是“谁依赖谁”,还要回答“为什么这么分层”。AI 需要判断:
- 哪些模块属于入口层(Controller、API、界面)。
- 哪些模块属于应用层(用例、服务编排)。
- 哪些模块属于领域层(核心业务逻辑)。
- 哪些模块属于基础设施层(数据库、消息队列、第三方 SDK)。
分层判断是 AI 读代码出图的核心价值,也是最容易出现偏差的地方。
2.4 阶段四:图渲染与输出
建模完成后,AI 需要把结果输出为某个具体格式的架构图。常见的选择有:
| 格式 | 特点 | 适用场景 |
|---|---|---|
| Mermaid | 文本即图,支持 Markdown 内嵌 | 文档、Wiki、GitLab/GitHub |
| PlantUML | 类 Java 语法,适合 UML 图 | 正式设计文档 |
| D2 | 语法简洁,布局美观 | 新项目首选 |
| Draw.io(XML) | 可继续手动拖拽编辑 | 团队有手动编辑需求 |
| ASCII 图 | 纯文本,适合快速预览 | 终端环境、临时沟通 |
推荐优先支持 Mermaid 和 Draw.io 两种格式:Mermaid 适合自动生成和版本管理,Draw.io 适合需要人工继续微调的团队。
3. 认识 Skill:一个能被 AI 加载的“技能包”
3.1 Skill 是什么
这里说的 Skill,是 AI Agent 生态中的一种可复用能力封装。简单理解:Skill 是一组带说明文件、提示词脚本和辅助工具的目录,AI 在对话中会根据用户意图自动加载并执行。
它和普通 Prompt 的区别在于:
- Prompt 只有文字描述,Skill 还可以携带脚本、模板、数据文件。
- Prompt 每次都要重新写,Skill 可以重复使用、跨项目共享。
- Prompt 依赖用户手动提供上下文,Skill 可以主动调用命令读取代码、执行扫描。
对于“AI 读代码出架构图”这个需求,Skill 是一个非常合适的载体:把读代码的策略、依赖提取脚本、出图标准全部封装起来,使用者只需要触发一次。
3.2 Skill 的目录结构
目前主流 AI 编程工具的 Skill 目录一般遵循这样的规范:
skills/ └── archi-mapper/ ├── SKILL.md # 技能说明,AI 首先读取的入口文件 ├── prompts/ # 分步执行的提示词模板 │ ├── 01-scan-code.md │ ├── 02-model-deps.md │ └── 03-render-diagram.md └── scripts/ # 可执行的辅助脚本 ├── scan_imports.py └── build_tree.py3.3 SKILL.md 的关键作用
SKILL.md的作用是让 AI 判断“什么时候该用这个技能”以及“怎么按步骤执行”。它通常分为两部分:
- 元数据区:声明技能名称、描述、触发关键词。
- 正文区:说明前置条件、执行步骤、输出规范、注意事项。
当用户在对话中说出“画一下这个项目的架构图”时,AI 会匹配技能描述,然后加载SKILL.md中的执行步骤。
3.4 为什么需要配套脚本
有人可能会问:AI 本身就能读代码,为什么还要写脚本?
原因是效率和准确性。AI 的上下文窗口是有限的。让 AI 逐个文件读代码,既慢又容易遗漏。通过脚本先做一轮机械扫描,得到目录树、依赖列表等结构化数据,再把这些数据交给 AI 做语义分析,可以大幅提高准确率。
脚本是 AI 的“眼睛”,Prompt 是 AI 的“大脑”。两者配合,才能稳定输出高质量的架构图。
4. 实战:从零编写架构图生成 Skill
下面进入实战环节。我们以 Claude Code 环境为例,写一个名为archi-mapper的 Skill,目标是让 AI 读取一个 Python 项目的代码,自动生成 Mermaid 格式的架构图。
4.1 创建目录结构
mkdir -p ~/.claude/skills/archi-mapper mkdir -p ~/.claude/skills/archi-mapper/prompts mkdir -p ~/.claude/skills/archi-mapper/scripts如果你使用的是项目级 Skill,也可以将archi-mapper放在项目的.claude/skills/目录下,这样只有该项目会加载该技能。
4.2 编写 SKILL.md
文件路径:~/.claude/skills/archi-mapper/SKILL.md
--- name: archi-mapper description: 读取当前代码仓库的目录结构和模块依赖,生成架构图。当用户提到"画架构图""生成依赖图""梳理模块关系""分析项目结构"时自动触发。 --- # 架构图自动生成技能 这个技能帮助你快速理解一个代码仓库,并输出可视化的架构图。 ## 前置条件 - 当前目录是一个代码仓库,或者用户指定了代码路径。 - 代码语言需要能被 Python 脚本扫描(支持常见语言,重点适配 Python/Java/TypeScript)。 ## 执行步骤 1. 使用 `scan_imports.py` 扫描代码仓库,获取文件列表和依赖关系。 2. 阅读扫描结果,结合项目 README 和关键配置文件,判断模块边界。 3. 将模块按层次归类:入口层、应用层、领域层、基础设施层。 4. 输出以下两种产物: - `architecture.json`:结构化架构描述。 - `architecture.md`:包含 Mermaid 架构图的 Markdown 文档。 ## 输出规范 - 架构图必须分层展示,同一层的模块放在一个分组中。 - 依赖关系必须注明方向,避免出现循环依赖环。 - 如果存在循环依赖,在输出报告中额外标注警告。 - 模块命名优先使用代码中的实际包名或目录名。这份SKILL.md遵循了“少量元数据、清晰步骤、明确输出规范”的原则。AI 加载这个文件后,就知道自己应该先做什么、怎么做、最终输出什么。
4.3 编写代码扫描脚本
为了让 AI 高效获取代码结构,我们需要一个 Python 脚本,用于提取目录树和 import 依赖关系。
文件路径:~/.claude/skills/archi-mapper/scripts/scan_imports.py
#!/usr/bin/env python3 import ast import json import os import sys from collections import defaultdict from pathlib import Path def should_ignore(path: Path) -> bool: """判断是否应该跳过该文件或目录。""" ignored = { '.git', '__pycache__', 'node_modules', 'venv', '.venv', 'dist', 'build', '.idea', '.vscode', 'target' } parts = set(path.parts) return bool(parts & ignored) or path.name.startswith('.') def scan_python_imports(root: Path) -> dict: """扫描 Python 文件的 import 依赖。""" deps = defaultdict(set) for path in root.rglob('*.py'): if should_ignore(path): continue try: tree = ast.parse(path.read_text(encoding='utf-8')) except Exception: continue for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: deps[str(path)].add(alias.name.split('.')[0]) elif isinstance(node, ast.ImportFrom): if node.module: deps[str(path)].add(node.module.split('.')[0]) return {k: sorted(v) for k, v in deps.items()} def build_tree(root: Path, max_depth: int = 3) -> dict: """构建目录树,限制层数避免输出过大。""" result = {} try: children = sorted( [p for p in root.iterdir() if not should_ignore(p)], key=lambda p: (p.is_file(), p.name) ) except PermissionError: return result for child in children: if child.is_dir(): if max_depth > 0: result[child.name] = build_tree(child, max_depth - 1) else: result.setdefault('__files__', []).append(child.name) return result def main(): root = Path(sys.argv[1] if len(sys.argv) > 1 else '.') output = { 'root': str(root.resolve()), 'tree': build_tree(root, max_depth=3), 'imports': scan_python_imports(root), } print(json.dumps(output, ensure_ascii=False, indent=2)) if __name__ == '__main__': main()这个脚本做的事情很明确:
build_tree()生成最多三层的目录树,避免把整个项目结构一次性塞给 AI。scan_python_imports()用 Python 自带的ast模块解析 import 语句,提取文件级依赖。- 最终输出 JSON,结构化数据比原始代码更适合 AI 分析。
在命令行中运行:
cd /path/to/your/project python3 ~/.claude/skills/archi-mapper/scripts/scan_imports.py . > code-map.json预期输出类似:
{ "root": "/path/to/your/project", "tree": { "app": { "__files__": ["main.py", "config.py"], "services": { "__files__": ["order_service.py", "user_service.py"] } }, "domain": { "__files__": ["models.py", "exceptions.py"] } }, "imports": { "app/main.py": ["config", "services"], "app/services/order_service.py": ["domain"] } }有了这份 JSON,AI 不需要浏览全部代码就能了解到“谁依赖谁”的骨架信息。
4.4 编写分步执行 Prompt
在prompts/目录下,我们准备三个步骤提示词。AI 会按照SKILL.md中声明的顺序,读取并执行这些提示词。
文件路径:~/.claude/skills/archi-mapper/prompts/01-scan-code.md
# 步骤一:扫描代码结构 运行以下命令获取项目的代码地图: ```bash python3 ~/.claude/skills/archi-mapper/scripts/scan_imports.py <项目根目录> > code-map.json读取code-map.json后,确认以下信息:
- 项目的顶层目录有哪些,分别承担什么职责。
- 哪些目录属于入口层,哪些属于服务层,哪些属于数据层。
- 关键入口文件在哪里(如
main.py、application.py、index.js)。
不要盲目阅读所有代码,只阅读与模块边界判断相关的关键文件。
文件路径:`~/.claude/skills/archi-mapper/prompts/02-model-deps.md` ```markdown # 步骤二:建模模块依赖 基于 `code-map.json` 的 import 关系,结合关键模块的代码阅读结果,整理出模块依赖列表。 输出要求: - 每个模块一个节点,使用真实目录名或包名。 - 每条依赖必须写明方向:A -> B 表示 A 依赖 B。 - 筛掉无关的第三方库依赖,只保留项目内部模块关系。 - 将依赖按层次归类:入口层、应用层、领域层、基础设施层。 如果发现循环依赖,单独列出,并在最终报告中给出警告说明。文件路径:~/.claude/skills/archi-mapper/prompts/03-render-diagram.md
# 步骤三:渲染架构图 根据上一步建立的模型,输出 `architecture.json` 和 `architecture.md`。 `architecture.json` 结构示例: ```json { "modules": [ { "name": "app", "layer": "presentation", "description": "HTTP 入口和路由配置" } ], "dependencies": [ { "from": "app", "to": "services" } ] }architecture.md中必须包含一段 Mermaid 代码块,使用 subgraph 分组表示层次,箭头表示依赖方向。
示例片段(AI 需要按实际项目调整):
graph TD subgraph presentation[入口层] app[app] end subgraph application[应用层] services[services] end subgraph domain[领域层] domain[domain] end app --> services services --> domain注意:
- 如果项目规模较大,按功能域拆分成多张子图,不要画一张超大图。
- 图中节点不超过 20 个,超过时优先按模块聚合。
- 最后必须补充一段文字说明,解释各层职责和关键依赖方向。
### 4.5 在 Claude Code 中调用与验证 安装完成后,在 Claude Code 会话中输入: ```text 请帮我梳理一下当前项目的架构,并生成架构图。AI 检测到“架构图”关键词后,会自动加载archi-mapper技能,然后依次执行扫描、建模、渲染三个步骤。
如果一切正常,当前项目目录下会出现:
architecture.json architecture.md打开architecture.md,就能看到带 Mermaid 架构图的文档。在支持 Mermaid 渲染的编辑器或 GitLab/GitHub 中会直接显示为图形。
整个过程中,你不需要手动指定任何依赖关系,也不需要拖拽任何图形元素。AI 读完代码后直接出图。
5. 让 AI 读懂代码的三个关键技巧
5.1 控制上下文:不要一次性喂全量代码
AI 的上下文窗口是有限的。一个中大型项目可能有几千个文件,全部塞进去既不现实也没必要。正确做法是:
- 用脚本把目录树和依赖关系压缩成 JSON。
- 只让 AI 阅读关键文件,例如入口文件、核心模型、配置中心。
- 约定扫描深度,避免把第三方源码、生成代码、测试代码也纳入分析。
换句话说,脚本负责广度,AI 负责深度。脚本保证不遗漏大结构,AI 集中精力理解关键节点的语义。
5.2 让 AI 输出结构化中间结果
架构图生成不是一步到位的。中间结果越结构化,最终的图就越稳定。这也是我们在prompts中强制要求输出architecture.json的原因。
结构化结果有三个好处:
- 便于人工审查:看 JSON 就能知道 AI 对模块边界的判断是否正确。
- 便于自动化:CI 流程可以解析 JSON,自动生成架构图。
- 便于后续对话:用户可以在 JSON 基础上提出调整,不需要让 AI 重新读一遍代码。
建议在 Skill 中明确输出 JSON Schema,即使一开始不完美,也比让 AI 自由发挥要可靠得多。
5.3 为架构图选择标准模型
“架构图”这个词太宽泛。AI 生成前,最好明确图的类型。常用的有:
- 模块图:展示顶层模块划分与依赖,适合快速概览。
- 分层图:展示入口层、应用层、领域层、基础设施层,适合介绍系统设计。
- C4 模型:Context(上下文)、Container(容器)、Component(组件)、Code(代码),适合从多个粒度描述系统。
如果团队已经使用了 C4 模型,可以在 Skill 中预设 C4 的层级模板,让 AI 按 Context 图和 Container 图分别输出。这样生成的图在团队内部有统一标准,不会出现“每个人画的风格都不一样”的问题。
6. 常见问题与排查思路
在实际使用这套 Skill 的过程中,可能会遇到一些典型问题,这里整理成表格供快速排查。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| AI 没有自动触发 Skill | 对话内容未命中描述中的关键词 | 在description中补充更多触发词,或手动指定技能名称 |
| 生成的架构图只有目录树,没有依赖关系 | 扫描脚本未成功提取 import | 检查脚本输出,确认 Python 文件语法正确,排除文件编码问题 |
| 依赖关系大量缺失 | 项目使用了动态导入、反射或服务发现 | 在 Prompt 中要求 AI 阅读配置文件,补充动态注册的依赖 |
| 图太大、节点太多 | 项目模块数量超过 AI 的简化阈值 | 在 Prompt 中强制要求按业务域聚合节点,或者拆分为多张子图 |
| 分层判断不准确 | 项目架构本身不规范,没有清晰层次 | 结合 README、架构文档、DDD 设计说明进行人工修正并反馈 |
| Mermaid 渲染报错 | 节点名包含特殊字符,或存在重复节点 | 在 Prompt 中约定节点 ID 使用英文小写加下划线,避免中文和空格 |
| 扫描脚本超时 | 代码库过大,或存在超大文件 | 在脚本中加入超时控制和文件大小限制,只扫描关键目录 |
| 循环依赖警告过多 | 测试代码与业务代码未分离 | 在脚本中增加忽略测试目录的规则 |
如果 AI 生成的图和你预期相差较大,最好的方式不是反复修改 Prompt,而是先修正architecture.json,再让 AI 重新渲染。因为 JSON 是中间产物,修正它比让 AI 重读代码效率高得多。
7. 最佳实践与工程建议
7.1 从小项目开始试点
不要一上来就在超大项目上测试。建议先在 5 到 10 个模块的中小型项目中试用,人工检查 AI 的模块划分和依赖理解是否准确。等提示词和脚本打磨稳定后,再逐步扩展到更大的项目。
7.2 保留人工复核环节
AI 读代码出图再高效,也不能完全替代架构师的人工判断。特别是以下场景,人工复核至关重要:
- 模块边界涉及业务领域知识时。
- 历史遗留代码存在架构腐化时。
- 依赖关系隐藏在异步消息、事件驱动中时。
比较推荐的做法是:AI 生成初稿,架构师审查并修改architecture.json,然后将修正后的版本回传给 AI 重新渲染。这样既发挥了 AI 的效率,也保留了人的经验。
7.3 将架构图纳入版本管理
建议把architecture.md、architecture.json以及扫描结果一起提交到 Git 仓库。每次代码变更后,可以通过 CI 任务自动更新架构图。这样做的好处是:
- 代码评审时可以直观看到改动影响的范围。
- 新人入职后可以快速了解项目全貌。
- 架构图的历史版本可追溯,方便复盘架构演进。
7.4 为 Skill 增加更多语言支持
本文的示例脚本只处理了 Python 的 import。对于 Java 项目,可以使用类似思路解析 import 语句;对于 TypeScript 项目,可以解析 import/require。不需要把脚本写得很完美,只要能为 AI 提供“候选依赖列表”即可,真正的过滤和归类和判断可以交给 AI 完成。
7.5 结合团队规范定制输出
每个团队对架构图的规范可能不同。有的团队要求必须包含技术选型,有的团队要求标注服务负责人,有的团队要求生成 C4 模型。这些都可以通过修改SKILL.md的输出规范和prompts目录中的模板来实现。Skill 的优势正在于此:它不是一段固定 Prompt,而是一套可以按团队需求持续迭代的工程资产。
写在最后
让 AI 边读代码边出图,核心价值不是“自动生成一张图”,而是让架构图真正成为代码库的可视化投影。每一次代码变更后,你都能用同样的方式重新生成图,保证图与代码同步演化,而不是画完就变成历史文物。
从工程落地角度看,最关键的并不是写一段多复杂的 Prompt,而是设计好“脚本扫描 + AI 分析 + 结构化中间产物 + 最终渲染”这条链路。脚本保障广度,AI 负责深度,JSON 保留可审查的中间结果,最后的 Mermaid 图则承担沟通表达职能。把这个链路封装成 Skill 之后,团队里任何人说一句“帮我看下项目架构”,就能得到一份相对靠谱的架构图初稿。
如果你也想在自己的项目里试试,建议从本文的archi-mapper目录结构出发,先跑通一个最小的 Python 项目,再逐步调整提示词和脚本。过程中遇到架构判断不准的问题,记得先修正中间 JSON,再重新出图,而不是反复让 AI 重读代码。这套方法真正用顺之后,你会发现:架构图的维护成本,终于可以降下来了。