ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

架构图Agent:从源码到Mermaid的自动化生成

架构图Agent:从源码到Mermaid的自动化生成 架构图 Agent 这个说法最近在 GitHub 上频繁出现。它不是某个单一仓库的定义而是一类项目的统称用 Agent 的方式自动理解代码、文档、配置文件甚至一段自然语言描述再生成 Mermaid、PlantUML 这类图语言文本最后渲染成架构图。开发者会为这类项目停留通常不是因为图本身画得多么炫而是因为它把“维护架构图”这件事从每周手工更新变成了可提交到仓库的文本产物。真正麻烦的从来不是画一个矩形和箭头而是让图跟上代码的变化。这篇文章不绑定某一个具体仓库而是围绕架构图 Agent 的通用技术链路展开。先讲它解决什么问题再拆解从信息源到图语言的核心流程然后用一个最小 Python 示例跑通“源码分析 - Mermaid - PNG”最后给出升级成 LLM Agent、生产落地、常见排错和选型建议。1. 架构图 Agent 解决的不是“画图”问题而是“信息保鲜”问题1.1 手工维护架构图的真实痛点画图五分钟维护一个月很多团队不是没有架构图而是架构图从创建那一刻起就开始过期。一个新服务上线或者一次数据库拆分架构图可能就要改十几个节点。更麻烦的是不同人维护的图粒度不一致有人画容器级有人画类级有人只在出事时才想起补图。这个问题在微服务架构里尤其明显。假设系统有 20 个服务每个服务背后是一个独立代码仓库。某个服务调用关系变化时需要手工打开多个仓库核对代码确认接口路径、依赖方向和部署环境。这时候真正消耗时间的不是画图软件里的拖拽操作而是信息收集和确认。所以手工维护架构图的本质问题是信息的“保鲜成本”。代码是唯一的事实来源但架构图是另一个事实来源两者的同步完全依赖人工。只要同步频率低于代码变更频率图就会失真。架构图 Agent 想解决的问题就是把这个同步过程自动化。1.2 Agent 与普通绘图工具、脚本生成器的本质区别先区分三类工具。普通绘图工具例如 draw.io、ProcessOn、Visio解决的是“我已经知道结构怎么画得好看”。它们提供丰富的图形组件和排版能力但不理解对象关系也不关心代码是否变化。脚本生成器例如根据 Kubernetes Deployment 自动生成拓扑图的工具解决的是“从某一种固定数据源生成图”。只要数据源格式正确就能输出图但范式固定换一种输入就失效。架构图 Agent 则更接近“具有观察、决策和工具调用能力的自动化流程”。它可以接收异构信息例如源码、README、OpenAPI 文档、注册中心数据、链路追踪信息然后规划先做哪一步、调用什么工具、如何校验结果、是否重新生成。它不依赖某一种单一输入而是把多种信息源汇成一个结构化模型再转换成图。可以用一张表快速区分类型典型工具输入是否理解系统是否持续更新失败处理普通绘图工具draw.io、Visio人工录入否只能靠人人工修正脚本生成器固定格式解析脚本特定数据源部分理解格式需重新触发格式错误即中断架构图 Agent结合 LLM 与代码分析的项目源码、文档、配置、自然语言能抽取语义可集成 CI 自动运行工具调用失败后重新规划绘图工具适合一次性表达能力脚本生成器适合稳定数据源Agent 适合持续变化的代码库。1.3 这类项目在 GitHub 受到关注的原因GitHub 上这类项目热度高不只是一个技术尝鲜的问题而是它踩中了几个真实需求。第一架构图进入版本管理。Mermaid 和 PlantUML 都是纯文本可以放进 Git 仓库可以打开 PR 做 diff review。这比图片文件更适合团队协作。Agent 生成的是文本图语言天然满足这个要求。第二LLM 的代码理解能力已经足够处理常见场景。模块划分、依赖方向、接口调用、部署关系这些信息在代码中都有明确表达。模型不需要理解全部业务逻辑只需要把显式依赖抽取出来并整理成图。第三Agent 可以嵌入 CI。代码合并后自动刷新架构图PR 里能看到结构变化。这正是“故事”动人之处开发者不再需要为了更新文档打断开发流而是让机器人去做重复劳动。但这里也要清醒一点。架构图 Agent 不是银弹。它不能凭空知道未建模的依赖也不能替架构师做设计决策。它在 GitHub 上受关注是因为它把最机械的部分自动化了而不是因为它能替代架构设计。理解这一点之后看代码实现就有正确预期。2. 架构图 Agent 的核心链路从信息源到图语言2.1 输入形态决定第一层解析策略架构图 Agent 的输入端很杂不同输入需要不同解析策略。如果一开始就让 LLM 读全量代码token 消耗会迅速失控。更合理的做法是先按输入类型选择解析路径。源码是主要输入。Python、Java、Go、TypeScript 都有成熟 AST 解析库。通过 AST 可以提取 import、类定义、函数调用、接口实现等结构信息。源码分析的结果非常确定适合作为 Agent 的“证据基础”。文档和注释是次要不完整输入。README 里的模块清单、接口文档里的调用关系、代码注释里标记的架构决策都可以用 LLM 抽取。但这类信息容易过期只能作为补充不能作为唯一依据。运行时数据是另一种重要输入。注册中心里的服务实例列表、Kubernetes Deployment 的副本数和依赖环境、链路追踪里的调用路径能反映线上真实结构。Agent 可以通过脚本或 API 拉取这些数据生成部署视图或运行时拓扑图。输入类型典型来源解析方式能拿到什么源码Git 仓库AST parser模块、类、函数、import 依赖文档README、架构说明LLM 抽取模块职责、边界、设计约定配置Dockerfile、CI、KubernetesYAML/JSON 解析部署关系、环境依赖运行时数据Nacos、Consul、SkyWalkingAPI/脚本服务实例、调用链、负载关系在架构设计上应该先做一次代码结构扫描形成基线再让 Agent 在基线上做增量补充。这样既能减少模型幻觉也能控制成本。2.2 用结构化中间表示隔离“理解”和“输出”架构图 Agent 内部不应该直接保存 Mermaid 文本而应该先构建一个独立于图语言的中间结构。这个结构类似一个架构模型 JSON描述对象、属性和关系。例如一个最小模型{ modules: [ { name: order-service, type: service, language: java, path: services/order-service }, { name: payment-service, type: service, language: java, path: services/payment-service } ], dependencies: [ { from: order-service, to: payment-service, type: http, description: create payment } ] }为什么需要中间层第一图语言有很多种Mermaid、PlantUML、Graphviz 语法不同统一模型可以支持多格式输出。第二模型可以校验和过滤。例如去重、合并同名节点、筛掉明显错误的关系。第三可以做增量比较。当前版本的结构模型与上一次提交的结构模型对比能自动发现架构漂移。中间表示是整个 Agent 的“工作内存”。理解了输入后Agent 把它规整成结构化数据再根据用户需要的视图类型生成图。2.3 图语言选型不要为了炫技引入难维护的格式生成架构图前会先选图语言。不同图语言有不同特点对 Agent 生成影响很大。Mermaid 语法相对简单支持 flowchart、sequenceDiagram、classDiagram、stateDiagram。GitHub 原生支持 Mermaid 渲染PR 里可以直接显示非常适合协作。它的稳定性在中大型图上一般节点太多会显得拥挤但作为 Agent 生成的默认格式足够。PlantUML 表达力强支持更精细的布局控制尤其在 UML 类图和时序图方面。缺点是需要安装 PlantUML 渲染环境和 Java 依赖GitHub Preview 需要插件或第三方服务。Graphviz 的 DOT 语言适合布局算法复杂的图尤其适合大层级结构。但 DOT 语法对新手不友好生成文本的可读性不如 Mermaid。图语言适合图形渲染方式协作体验Agent 生成难度MermaidFlowchart、类图、时序图GitHub 原生支持很好低PlantUMLUML 类图、时序图、部署图需要 PlantUML 环境中等中Graphviz DOT大层级图、依赖图需要 graphviz 环境一般中高我的建议是默认用 Mermaid只有当业务明确需要精细 UML 语义时再上 PlantUML。架构图 Agent 的核心价值是自动生成和持续维护而不是一次性的排版精美。越简单的文本格式越容易校验和 review。2.4 输出物应该是文本而不是只能打开的图片很多开发者第一次接触这类 Agent 时期待它直接输出一张 PNG。但工程上更合理的输出是图语言文本再按需渲染成图片。文本图语言有三点优势。一是可 diff。PR 里能看到“哪里增加了依赖、哪里删除了节点”这是图片做不到的。二是可复用。团队可以使用同一份结构数据渲染成不同风格架构评审时用 SVG写文档时用 PNG丢进聊天工具时用 Markdown 链接。三是可自动化校验。文本可以写单元测试检查是否包含指定模块是否出现禁止依赖。所以架构图 Agent 的产物链建议是结构化模型 - 图语言文本 - 图片文件。每一层都可以被记录、审计和回滚。学习阶段尤其不需要追求最终图片先把文本生成正确渲染问题可以单独解决。3. 跑通最小示例Python AST 到 Mermaid 架构图3.1 环境准备和目录结构下面用一个最小示例说明整个链路。项目会用一个 Python AST 脚本扫描 sample_app 目录中的源码提取模块之间的 import 依赖生成 Mermaid flowchart再用 mermaid-cli 渲染成 PNG。目录结构如下arch-agent/ ├── analyzer.py ├── mermaid_writer.py ├── sample_app/ │ ├── __init__.py │ ├── order.py │ ├── payment.py │ └── user.py └── output/ └── architecture.mmd本机需要准备 Python 3.9 以上环境。建议创建虚拟环境mkdir arch-agent cd arch-agent mkdir sample_app output python3 -m venv .venv source .venv/bin/activate渲染 Mermaid 需要 mermaid-cli它依赖 Node.js 和浏览器组件。安装命令npm install -g mermaid-js/mermaid-cli如果本机已经使用 npx也可以临时调用不全局安装npx -y mermaid-js/mermaid-cli -i output/architecture.mmd -o output/architecture.png -b white这里要注意npx 首次执行会下载工具包耗时较长。生产环境建议固定版本或使用 CI 镜像避免每次运行时发生版本漂移。3.2 示例代码三个简单的 Python 模块创建三个模拟模块。order.py 依赖 payment 和 user# sample_app/payment.py def create_payment(user_id: str, amount: int): return {user_id: user_id, amount: amount}# sample_app/user.py def get_user(user_id: str): return {id: user_id}# sample_app/order.py from payment import create_payment from user import get_user def create_order(user_id: str, amount: int): user get_user(user_id) payment create_payment(user_id, amount) return {user: user, payment: payment}这三个文件虽然简单但已经具备模块依赖关系。Agent 脚本要做的事就是识别出order依赖payment和user。3.3 用 AST 提取模块依赖创建 analyzer.py扫描目录下所有 Python 文件用 AST 解析 import 语句并建立模块依赖模型。import ast import argparse import pathlib from typing import List, Dict def list_python_files(root: pathlib.Path) - List[pathlib.Path]: return [p for p in root.rglob(*.py) if p.is_file()] def build_module_model(root: pathlib.Path) - Dict: files list_python_files(root) module_names {p.stem for p in files} model { modules: [], dependencies: [], } for py_file in files: try: tree ast.parse(py_file.read_text(encodingutf-8), filenamestr(py_file)) except SyntaxError as exc: print(f[warn] skip {py_file}: {exc}) continue module_name py_file.stem deps set() for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: first_part alias.name.split(.)[0] if first_part in module_names: deps.add(first_part) elif isinstance(node, ast.ImportFrom): if node.module: first_part node.module.split(.)[0] if first_part in module_names: deps.add(first_part) model[modules].append({ name: module_name, path: str(py_file), }) for dep in sorted(deps): model[dependencies].append({ from: module_name, to: dep, }) return model def main(): parser argparse.ArgumentParser(descriptionAnalyze module dependencies) parser.add_argument(root, helproot directory of Python source) parser.add_argument(--output, helpoutput JSON file, defaultoutput/model.json) args parser.parse_args() model build_module_model(pathlib.Path(args.root)) pathlib.Path(args.output).parent.mkdir(parentsTrue, exist_okTrue) pathlib.Path(args.output).write_text( __import__(json).dump(model, ensure_asciiFalse, indent2), encodingutf-8 ) if __name__ __main__: main()这段代码的核心是用ast.walk遍历所有节点识别Import和ImportFrom取出第一个点号分段。只有当模块名存在于当前扫描结果中才记录为内部依赖。这一步是为了避免把第三方包也画进架构图。运行命令python analyzer.py sample_app --output output/model.json会生成output/model.json内容包含三个 module 和两条 dependency。3.4 把结构模型写成 Mermaid 文本创建 mermaid_writer.pyimport argparse import json import pathlib from typing import Dict def to_mermaid(model: Dict) - str: lines [flowchart LR] for module in model[modules]: name module[name] safe_id name.replace(-, _) lines.append(f {safe_id}[{name}]) for dep in model[dependencies]: lines.append(f {dep[from]} -- {dep[to]}) return \n.join(lines) \n def main(): parser argparse.ArgumentParser(descriptionConvert JSON model to Mermaid) parser.add_argument(input, helpinput JSON file) parser.add_argument(--output, defaultoutput/architecture.mmd, helpoutput mermaid file) args parser.parse_args() with open(args.input, encodingutf-8) as f: model json.load(f) pathlib.Path(args.output).parent.mkdir(parentsTrue, exist_okTrue) pathlib.Path(args.output).write_text(to_mermaid(model), encodingutf-8) print(to_mermaid(model)) if __name__ __main__: main()运行python mermaid_writer.py output/model.json --output output/architecture.mmd预期输出flowchart LR order[order] payment[payment] user[user] order -- payment order -- user这里的flowchart LR表示从左到右布局的流程图。节点使用双引号显示名称避免模块名中出现特殊字符时破坏 Mermaid 语法。3.5 渲染成 PNG 并验证使用 mermaid-cli 渲染npx -y mermaid-js/mermaid-cli -i output/architecture.mmd -o output/architecture.png -b white验证点有三个。第一命令是否成功执行没有 Parse error。如果报语法错误先打开.mmd文件检查节点名是否包含括号、冒号、中文等字符。第二PNG 中是否出现三个节点和两条实线箭头。箭头从order指向payment和user方向应与代码中的 import 关系一致。第三把.mmd文件提交到 Git确认 GitHub 能直接渲染。如果 GitHub 页面没有预览通常是文件后缀不是.mmd或者内容前几行格式不对。这个最小示例没有使用 LLM但已经展示了 Agent 的一条关键链路用确定性的工具先拿到结构化证据再基于结构生成图。LLM 的作用会在下一步接入而不是替代所有规则。4. 把规则脚本升级成 LLM Agent 的工程思路4.1 为什么不能停在 AST 规则脚本AST 脚本的优点是可解释、结果确定、成本低。但它只能看到机械结构。比如order.py中出现了from payment import create_payment它知道order依赖payment但它不理解“创建订单后需要调用支付服务完成扣款”这样的业务语义。真实项目里很多依赖关系并不是通过直接 import 表达的。服务 A 可能通过 HTTP 调用服务 B代码里只出现了一个 URL 常量组件 A 可能通过消息队列订阅组件 B 的事件代码里没有强类型引用。AST 对这类关系是盲区。LLM 的补充价值在于可以阅读更广泛的信息例如 README 中的架构说明、接口文档中的调用关系、配置文件中注册的路由。它能把自然语言描述转换成结构化依赖也能在 AST 证据基础上推理出 HTTP 调用边和消息边。但是 LLM 会制造幻觉。它可能根据常识补齐一个不存在的依赖也可能把同名不同模块的节点合并。所以工程上应该保留 AST 作为证据第一来源LLM 作为补充关系提取器再通过校验规则过滤结果。4.2 Agent 的运行循环规划、工具调用、反思一个完整架构图 Agent 的运行循环可以抽象成四步。第一步观察。Agent 调用工具读取仓库目录、分析 AST、读取关键文档。这个过程生成“观察结果”也就是上文的结构化模型。第二步规划。LLM 根据当前观察结果决定下一步动作。比如发现order-service里有一个 HTTP 调用字符串但还没找到目标服务模型可以规划调用搜索工具去查服务注册表。第三步执行。Agent 调用预先定义好的函数而不是让模型直接写代码。例如提供read_file、search_symbol、append_dependency等工具。工具返回值会成为新的观察结果。第四步反思。模型检查当前结构模型是否完整、是否存在冲突。如果发现依赖缺失就回到第一步继续如果认为目标已经达成就调用write_mermaid输出结果。伪代码context load_context(repo_path) for step in range(max_steps): observation run_tools(context) context[observations].extend(observation) action llm_plan(context) if action[type] finish: write_mermaid(context[model]) break result execute_tool(action) context[tool_results].append(result)关键点是每一步都使用工具结果驱动而不是让模型在单次回复中凭空生成完整架构图。这样能显著降低错误率也让最终输出有证据可查。4.3 用函数约束模型输出而不是依赖提示词LLM 输出不可控尤其是生成 Mermaid 文本时很容易出现既不是流程也不是类图的混合语法。更稳妥的方式是用 Function Calling 接口让模型在预设的函数列表中选择和执行。例如注册一个写文件的工具{ name: write_mermaid, description: Write complete Mermaid diagram to target file, parameters: { type: object, properties: { path: { type: string }, content: { type: string } }, required: [path, content] } }模型只能调用这个函数不能自由输出任意 JSON。应用中可以先让模型调用finish_analysis并携带完整的 structured model再由后端脚本根据 model 生成 Mermaid。这样即使模型偶尔生成非法 Mermaid也不会进入最终产物。Prompt 可以这样写你是一个架构图 Agent。你的任务是根据工具返回的代码结构证据整理模块依赖列表。 规则 1. 只能使用工具返回的证据补充依赖。 2. 不要根据常识推测不存在的依赖。 3. 如果你发现证据冲突先记录冲突不要直接丢弃。 4. 只有所有依赖都有证据时才调用 finish_analysis。这种设计把模型的自由度控制在一个较窄的范围内适合生产环境。4.4 成本、延迟和失败控制LLM Agent 比 AST 脚本慢也比 AST 脚本贵。接入生产前需要想清楚控制策略。风险现象缓解方案token 超限任务中途失败上下文过大先用 AST 过滤只让模型读 diff 和关键配置模型死循环不停调用工具不结束设置最大步数默认 5 到 10 步输出非法 Mermaid渲染失败结构化模型生成 Mermaid不让模型直接写全文幻觉关系图里出现不存在的边要求每条边提供 file 和 line 证据费用不可控单次任务消耗大量 token分层规则能处理的用规则规则处理不了的才调 LLM比较好的做法是“先规则、后模型”。AST 和配置解析能覆盖 60% 到 80% 的依赖抽取剩下模糊关系再交给 LLM。这样成本低结果可控排错也更容易。5. 常见问题与排查路径5.1 Mermaid 语法解析失败现象是渲染时报Parse error on line X。常见原因是节点名包含特殊字符例如括号、冒号、连字符或者模块名直接使用了中文。处理方式是先在.mmd文件里定位报错行检查节点声明是否为id[显示名]格式。不要直接把模块名当 id 使用。例如order-service[order-service]比直接写order-service更安全。因为横线会被 Mermaid 解析成减号导致语法错误。如果报错是箭头方向写反例如payment -- order需要回到源码确认依赖方向。AST 结果不会骗人先核对model.json里的 dependency 方向。5.2 一张图上信息过载现象是图能渲染但节点太多连成一片看不出架构。根本原因是把所有维度揉进了一张图没有区分系统上下文、容器、组件、类这几种抽象层级。处理思路是参考 C4 model 分层。系统上下文图只画外部用户与系统边界容器图画服务、数据库、消息队列组件图画单个服务内部的模块代码级图只有某个组件需要时再生成。架构图 Agent 在设计时应该支持--level参数指定抽取深度。这样同一个仓库可以生成多个视图一个给老板看一个给开发 review一个给排障用。5.3 模型编造不存在的依赖边现象是图上出现一条代码中找不到任何证据的线。原因是 LLM 根据常识补齐了关系。例如订单服务通常依赖用户服务所以模型在没看到 import 时也会加上这条边。排查路径是先看该条依赖是否在结构模型中有file和line字段。如果没有证据就直接删除。生产 Agent 应该在 prompt 中明确要求所有依赖边必须关联到具体文件行号或者注明“来自配置文件”。另一个思路是对生成的图做静态校验。例如维护一个依赖白名单和黑名单白名单允许的边直接通过黑名单禁止的边立即告警。没有证据的边默认不显示。5.4 大仓库分析超时或 token 超限现象是 Agent 运行到一半停止日志显示 context length 超过限制或操作超时。原因是让模型直接读取了整个仓库。解决方法是先做缩小范围。只分析指定目录、指定模块或最近 git diff 涉及的文件。更完整的流程是用git diff --name-only获取变更文件。用 AST 分析变更文件得到基础模块图。把变更文件路径和 AST 结果传给模型让它补充业务语义关系。每批处理不要超过 20 个文件结果合并到同一个结构模型。如果仓库非常大还可以把扫描结果缓存到本地 JSON 或向量数据库只有代码变化时增量更新。下面是一个快速排查表问题现象常见原因检查方式处理建议Mermaid 解析失败节点名含特殊字符看mmdc报错行号用id[显示名]写法图太密多层级混在一张图统计节点数、边数增加--level分层出现不存在依赖模型补全常识关系检查依赖是否带 file/line要求证据添加白名单分析超时全量读代码查看输入 token 统计用 git diff AST 缩小范围6. 工程化落地建议与扩展方向6.1 在 GitHub 选型时如何判断一个 Agent 项目是否成熟GitHub 上架构图 Agent 项目很多判断标准不应只看 star 总量。更实际的维度是输入支持和输出约束。看项目是否支持多语言。只支持 Python 的项目换到 Java 仓库可能完全跑不动。看是否支持多种图语言。只输出 PlantUML 而团队用 GitHub 原生 Mermaid适配成本会高。看是否支持增量更新这决定它能不能在 CI 里稳定运行。看是否提供函数调用和工具约束。如果项目只是让 LLM 自由生成就很容易出现幻觉。还要看许可证和活跃度避免引入一个长期不维护的依赖。选型维度建议关注点输入解析是否使用 AST不只是正则图语言Mermaid / PlantUML / DOT 是否可配置LLM 接入是否支持 OpenAI 之外本地模型更新模式全量重扫还是增量输出校验是否有 Mermaid 语法校验、依赖证据CI 集成是否有 GitHub Actions 示例许可证是否能满足团队使用限制6.2 生产环境必须具备的五个能力架构图 Agent 从脚本变成服务至少要补齐五块能力。第一权限收敛。Agent 读取代码应该使用只读 token写入文件只允许输出目录。不要让模型有全局写权限否则一次幻觉可能导致批量文件被改写。第二输出校验。生成 Mermaid 后必须用语法解析器校验生成的结构模型也要跑单元测试。校验不通过不能自动提交。第三日志审计。记录每一步工具调用、每次 LLM 请求、最终生成文件。出问题时能快速定位是 AST 错误、模型幻觉还是渲染环境异常。第四降级方案。LLM 不可用时至少保留 AST 规则生成器。生产环境不能让架构图生成完全依赖外部模型服务。第五版本回滚。由于架构图文本进入 Git每次变更都有提交记录。发现错误后可以直接 revert不需要手动修图。6.3 后续可以继续做的扩展架构图 Agent 的下一步不只是画图。它可以把结构模型与代码评审打通在 PR 中提示“这个变更会引入 order-service 到 payment-service 的新依赖”帮助评审者看到架构影响。也可以做架构漂移检测。每次提交后自动比较当前结构模型与基线模型如果依赖数量、节点数量超过阈值就触发告警。这样 Agent 从“画图工具”变成了“架构守护工具”。多语言支持是长期工作。Python 的 AST、Java 的 JDT、Go 的 AST、TypeScript 的 ts-morph解析逻辑差异很大。建议先从主力语言入手把流程跑通再逐步增加语言适配层。离线环境可以接入本地模型例如 Ollama。数据不需要离开内网模型调用成本也更可控。代价是
返回列表