跳过正文
uml-generate 深度解析:用多厂商流式 LLM 一键生成 PlantUML(前端纯实现)
  1. 技术/

uml-generate 深度解析:用多厂商流式 LLM 一键生成 PlantUML(前端纯实现)

·5 分钟·
温州程序员劝退师
作者
温州程序员劝退师
目录

本文基于源码实读,拆解 uml-generate 的工程实现:如何在“纯前端”环境下,串起多家 LLM 的流式输出,并将结果即时转译为可编辑、可导出的 PlantUML 图表。

项目地址: https://github.com/GeekyWizKid/uml-generate

架构总览
#

纯前端 React + Vite 应用,直接请求各家 LLM API(支持流式),解析出文本中的 @startuml ... @enduml 片段,交由 PlantUML 渲染。

UI(App.jsx)
  ├─ 输入素材/选择 Provider/流式开关
  ├─ 文本输出(含生成中实时区域)
  └─ UML 输出(PlantUMLRenderer)

services/api.js
  ├─ Provider 适配(OpenAI/Claude/DeepSeek/Kimi/Gemini/Qwen/自定义)
  ├─ 普通/流式生成(统一回调增量文本)
  └─ Prompt 规范(顺序图/活动图/状态图 + 假设与占位符)

PlantUMLRenderer.jsx
  ├─ PlantUML 编码 + 多服务器回退(本地/第三方/官方)
  ├─ SVG/PNG 导出、复制代码、放大预览
  └─ 内嵌编辑器(编辑后局部替换并重新渲染)

关键文件:

  • src/App.jsx:1:主页面(输入、选择提供商、流式开关、结果展示)
  • src/services/api.js:1:统一调用层(多 Provider + 流式解析)
  • src/components/PlantUMLRenderer.jsx:1:PlantUML 渲染与工具链

多提供商流式适配
#

统一的 Provider registry 定义了 URL、请求头、请求体格式、响应提取与是否支持流式:

  • src/services/api.js:4(OpenAI/DeepSeek/Kimi,OpenAI 兼容)
  • src/services/api.js:19(Anthropic Claude)
  • src/services/api.js:46(Gemini,非流式)
  • src/services/api.js:64(通义千问,非流式)
  • src/services/api.js:81(自定义)

流式解析的核心在 generateUMLStream()

  • OpenAI 兼容(OpenAI/DeepSeek/Kimi):行以 data: {json} 到达,取 choices[0].delta.contentsrc/services/api.js:333)。
  • Claude:事件型流,识别 type=content_block_deltadelta.textmessage_stop 收尾(src/services/api.js:347)。
  • Gemini/Qwen:仅非流式,走 generateUML() 常规请求。

每次增量内容通过回调聚合到 fullContent 并回绑到 UI(src/App.jsx:56 流式状态管理)。

提示工程(Prompt)
#

buildUMLPrompt() 明确要求输出“六段结构”,并必须包含三类 UML 图(顺序、活动、状态),且强制使用 PlantUML 语法、不加 Markdown 围栏:

  • 位置:src/services/api.js:103
  • 重点:
    • 第一部分:核心要点与假设;
    • 第二、三、四部分:分别是顺序图、活动图、状态图,必要时以 TODO 占位;
    • 第五部分:占位符清单;第六部分:迭代建议;
    • 图块必须使用 @startuml@enduml 包裹,便于解析与渲染。

这种“强结构 + 最小必要图”策略,能在素材不完备时仍保持产出可用,并方便后续增量补齐。

PlantUML 渲染与回退
#

前端使用 plantuml-encoder 将 UML 源码压缩为 URL 片段,然后依次尝试多台服务器:

  • 本地 Docker:http://localhost:8080/svg/…start-plantuml.sh 快速拉起)
  • 第三方:https://plantuml-server.kkeisuke.dev/svg/…
  • 官方:https://www.plantuml.com/plantuml/svg/…

实现位于 src/components/PlantUMLRenderer.jsx:22

  • 逐个 fetch,30 秒超时(AbortSignal.timeout(30000))。
  • 命中 <svg 即视为成功,失败则继续回退(src/components/PlantUMLRenderer.jsx:41)。
  • 渲染失败时提供外链(SVG/PNG/在线编辑)与源码查看(src/components/PlantUMLRenderer.jsx:152)。

功能细节:

  • 放大预览:Portal 模态框(src/components/PlantUMLRenderer.jsx:296)。
  • 导出:SVG 直接保存;PNG 通过 Canvas 将 SVG 树转成位图(src/components/PlantUMLRenderer.jsx:78)。
  • 内嵌编辑:弹窗修改 PlantUML 代码后重新渲染,并仅替换被修改的 @startuml ... @enduml 块(src/components/PlantUMLRenderer.jsx:238)。

UI 与交互
#

  • 流式开关(src/App.jsx:43):便于预览生成进度、降低长文本超时概率。
  • 多 Provider 选择(src/App.jsx:156)与 Key 检查(src/App.jsx:65)。
  • 文本区与图表区实时联动:边生成边渲染(src/App.jsx:322src/App.jsx:388)。

安全与限制
#

  • API Key 存储在 localStorageapi.js:getStoredApiKeys()),属于“便利优先”的本地存储方案;若面向公网部署,建议:
    • 后端代理签名请求,前端不直带 Key;
    • 或至少做浏览器侧加密与过期(参考 writing-helper 里的 secureApiKey.ts 思路)。
  • CORS 由各厂商 API 决定(本项目纯前端,不提供自有代理)。若遇跨域限制,可临时走本地反向代理或 Vercel/Cloudflare Worker。
  • PlantUML 外部服务依赖网络环境;生产推荐自建 Docker 服务(仓库已提供 start-plantuml.sh)。

体验建议(可以做的增强)
#

  • 增加“类图/组件图”可选生成;
  • 对流式内容做“增量提取”避免重复回渲染全部 SVG;
  • 导出一键打包(Markdown + 图片);
  • 引入 simple rate limit(每秒最多 N 次生成),规避误触发封禁;
  • 允许自定义 PlantUML 主题与样式变量。

小结
#

uml-generate 在纯前端实现了“多提供商流式生成 + PlantUML 即时渲染”的最小可用闭环。其工程价值在于:

  • 通过 Provider 适配器,抹平各家流式差异;
  • 通过多服务器回退,显著提升渲染稳定性;
  • 通过强约束 Prompt,让输出结构规整、便于解析。

如果你的场景需要“更强安全”或“无跨域”,可以将本项目的前端 UX 与服务端代理结合,形成企业内可落地的“UML 生成助手”。

相关文章

writing-helper 深度解析:Next.js 15 流式多提供商写作助手(SSE + 统一代理)
·5 分钟
这篇文章基于源码实读,对 writing-helper 的架构与实现做“工程级”解析:如何在 Next.js 15 下,用统一代理层把 OpenAI / Grok / DeepSeek / Ollama 等提供商整合进一套流式(SSE)写作体验,并兼顾 CORS、安全与可用性。 本项目地址: writing-helper
GJavaDoc 深度解析:在 IDEA 内把遗留 Java 代码“清理出”可用接口文档
项目地址:GJavaDoc 本文基于源码逐行阅读的结果,对 GJavaDoc 的设计与实现做一份“工程级”的深度解析:为什么这样设计、关键权衡是什么、哪里容易踩坑,以及如何把它用在你自己的遗留项目里。 核心策略一句话概括——“事实交给程序,表达交给模型”。把能确定的事实(入口、签名、相关类型、代码片段、模块归属、历史产物)用静态分析和 PSI 拿稳,再让 LLM 按强约束的提示把事实“讲清楚”。这样文档才稳定、可复现、可增量、可并发生成。