1. 架构师画图这件事,为什么总在拖后腿
UML 类图和时序图是架构设计里绕不开的交付物,但真正动手画过的人都懂:需求评审刚过,代码还在改,图已经过期了。Visio、draw.io、PlantUML 各有各的痛——拖拽式工具改一次要动十几个框,纯文本工具语法记不住,最要命的是代码和文档两张皮,谁也不知道哪张图对应哪个版本。
Codex 这类代码理解型 AI 出现后,这件事有了新解法。它能直接读你的 Python、Java、TypeScript 源码,把类结构、继承链、方法调用关系抽出来,再按 PlantUML 语法生成.puml文件。你只需要描述清楚"要什么图、什么风格、什么关系类型",剩下的交给它。适合谁?正在做系统重构的架构师、需要给团队补文档的技术负责人、以及被"画图两小时改图五分钟"折磨过的后端同学。
这篇不讲虚的,直接给可复制的 Codex 提示词模板、PlantUML 本地渲染配置、逐图校验步骤,以及怎么通过 TaoToken 统一 Key 和 API 通道把调用跑通。电商订单、支付回调这些真实场景会贯穿始终,每一步都能跟着做。
核心检索词先摆出来:Codex 生成 UML 类图、PlantUML 时序图、AI 自动画架构图。这三个词对应的能力,下面会拆成可执行的步骤。
2. TaoToken 前置:统一 Key 与 API 通道怎么配
在让 Codex 干活之前,得先把调用通道理顺。Codex CLI 本身支持自定义 Base URL 和 API Key,这意味着你可以把请求指向 TaoToken 的统一入口,用一个 Key 管理多个模型的调用。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数。
配置的核心是三件套:Base URL、API Key、Model ID。以 Codex CLI 为例,它的配置文件通常放在~/.codex/config.toml或项目根目录的.codex/config.toml。我试过在项目里放一份局部配置,这样不同项目可以用不同的模型和 Key,互不干扰。
先看配置文件的写法:
# .codex/config.toml model = "claude-sonnet-4-20250514" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"这里base_url填的是 TaoToken 的 API 根地址,env_key指定从哪个环境变量读 Key。接着在终端里导出 Key:
export TAOTOKEN_API_KEY="sk-你的实际Key"如果你用的是 Cline 或 Claude Code 这类工具,配置逻辑类似,但字段名不同。Cline 的 MCP 配置里需要写全 Base URL、Key、Model ID 三件套:
{ "mcpServers": { "taotoken": { "command": "npx", "args": ["-y", "@taotoken/mcp-server"], "env": { "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_API_KEY": "sk-你的实际Key", "TAOTOKEN_MODEL": "claude-sonnet-4-20250514" } } } }Codex 的auth.json方式也值得提一下,有些版本会把凭证存在~/.codex/auth.json:
{ "api_key": "sk-你的实际Key", "base_url": "https://taotoken.net/api" }配好之后,用一条最简单的命令验证通道是否通:
codex "用一句话说明什么是 PlantUML"如果返回正常文本,说明 Key 和 Base URL 都生效了。如果报 401,先检查环境变量有没有导出成功;如果报 local proxy failed,多半是 Base URL 写错或网络层拦截。这一步别跳过,后面所有 UML 生成都依赖这条通道。
关于 Key 的获取和更多接入方式,可以看接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc 。API Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys ,模型对话调试在 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat 。长期做编码和 Agent 任务的话,Coding Plan 更划算:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan 。
3. 可复制配置:Codex 提示词模板与 PlantUML 渲染环境
通道通了,接下来是让 Codex 按你的意图生成 PlantUML。这里的关键是提示词要结构化:说清楚输入是什么、输出什么图、关系怎么表达、风格怎么定。我整理了一套模板,直接复制改改就能用。
3.1 类图生成提示词模板
请分析以下代码,生成 PlantUML 类图,要求: 1. 提取所有类名、属性(带类型)、方法(带参数和返回值) 2. 识别继承、实现、组合、聚合、依赖关系,用正确的箭头符号 3. 可见性用 + - # 表示 public/private/protected 4. 抽象类加 abstract 关键字,抽象方法加 {abstract} 5. 使用蓝色主题,字体 14 号 6. 所有注释用中文 代码: [粘贴你的代码]把这段提示词和电商订单代码一起丢给 Codex,它会返回完整的@startuml ... @enduml块。实测下来,类图的结构准确率很高,尤其是继承和实现关系,基本不会错。组合和聚合偶尔会混,需要在提示词里强调"属性类型是另一个类时用组合,列表类型用聚合"。
3.2 时序图生成提示词模板
时序图比类图更依赖场景描述,因为方法调用链不是静态代码能完全表达的。模板如下:
请为"用户下单并支付"这个业务流程生成 PlantUML 时序图,参与者和交互如下: - 用户向购物车添加商品 - 用户确认下单,购物车调用订单服务创建订单 - 订单服务检查库存并扣减,生成订单号 - 订单服务调用通知服务发送下单通知 - 用户选择支付方式,订单服务调用支付接口 - 支付成功后订单服务更新状态并发送通知 - 订单服务返回支付结果给用户 要求:使用 autonumber 自动编号,激活框用 activate/deactivate,返回消息用虚线箭头,注释用中文。Codex 会输出带autonumber、activate、-->返回箭头的完整时序图。这里有个坑:如果参与者名字带空格,PlantUML 会解析出错,所以提示词里最好加一句"参与者名称用下划线代替空格"。
3.3 PlantUML 本地渲染配置
生成的.puml文件需要渲染成 PNG 或 SVG 才能看。本地环境需要 Java 11+、Graphviz、PlantUML 三样东西。macOS 和 Ubuntu 的安装命令:
# macOS brew install plantuml graphviz # Ubuntu sudo apt-get install plantuml graphviz # 验证 plantuml -version dot -V渲染单文件:
plantuml -tpng output/class_diagram.puml -o output/images/批量渲染整个目录:
plantuml -tpng output/*.puml -o output/images/VS Code 里装 PlantUML 插件后,打开.puml文件按Alt+D就能实时预览,改一行看一行,校验效率最高。如果你在 CI 里跑,用plantuml -tsvg生成矢量图,体积小还清晰。
3.4 项目结构建议
把生成、渲染、输出分开,目录结构清晰:
uml-generator/ ├── src/ # 代码分析逻辑 ├── examples/ # 示例代码(电商系统) ├── output/ # 生成的 .puml 文件 │ └── images/ # 渲染后的图片 ├── scripts/ │ └── render.sh # 批量渲染脚本 ├── Makefile # 一键操作 └── .codex/ └── config.toml # TaoToken 配置Makefile里定义几个常用目标:
.PHONY: all analyze render clean all: analyze render analyze: python main.py render: bash scripts/render.sh --format both clean: rm -rf output/*.puml output/images/*这样make all就能从代码分析一路跑到图片输出。配置和模板都齐了,下一节看实际跑出来的结果。
4. 验证请求:从电商订单代码到类图时序图
光有模板不够,得看真实代码跑出来的效果。这里用一段简化版电商系统代码做输入,覆盖订单、支付、通知三个核心域。
4.1 输入代码
from abc import ABC, abstractmethod from dataclasses import dataclass, field from enum import Enum from typing import Optional class OrderStatus(Enum): PENDING = "待支付" PAID = "已支付" SHIPPED = "已发货" CANCELLED = "已取消" @dataclass class Product: product_id: str name: str price: float stock: int def reduce_stock(self, quantity: int) -> bool: if self.stock >= quantity: self.stock -= quantity return True return False @dataclass class OrderItem: product: Product quantity: int unit_price: float @property def subtotal(self) -> float: return self.unit_price * self.quantity @dataclass class Order: order_id: str items: list[OrderItem] = field(default_factory=list) status: OrderStatus = OrderStatus.PENDING @property def total_price(self) -> float: return sum(item.subtotal for item in self.items) def add_item(self, product: Product, quantity: int) -> None: self.items.append(OrderItem(product, quantity, product.price)) class Payment(ABC): @abstractmethod def pay(self, order: Order) -> bool: pass @abstractmethod def refund(self, order: Order) -> bool: pass class AliPayment(Payment): def pay(self, order: Order) -> bool: order.status = OrderStatus.PAID return True def refund(self, order: Order) -> bool: order.status = OrderStatus.CANCELLED return True class WeChatPayment(Payment): def pay(self, order: Order) -> bool: order.status = OrderStatus.PAID return True def refund(self, order: Order) -> bool: order.status = OrderStatus.CANCELLED return True class NotificationService: def send_email(self, to: str, content: str) -> bool: return True class OrderService: def __init__(self, notification: NotificationService): self.notification = notification def create_order(self, order: Order) -> Optional[Order]: for item in order.items: if not item.product.reduce_stock(item.quantity): return None self.notification.send_email("user@example.com", f"订单 {order.order_id} 已创建") return order def pay_order(self, order: Order, payment: Payment) -> bool: success = payment.pay(order) if success: self.notification.send_email("user@example.com", f"订单 {order.order_id} 支付成功") return success4.2 生成的类图
把代码和类图模板一起发给 Codex,返回的 PlantUML 核心片段:
@startuml title 电商系统 - 类图 skinparam classAttributeIconSize 0 skinparam classFontSize 14 abstract class Payment { + {abstract} pay(order: Order): bool + {abstract} refund(order: Order): bool } class AliPayment { + pay(order: Order): bool + refund(order: Order): bool } class WeChatPayment { + pay(order: Order): bool + refund(order: Order): bool } class Order { + order_id: str + items: list[OrderItem] + status: OrderStatus + total_price: float + add_item(product: Product, quantity: int): None } class OrderItem { + product: Product + quantity: int + unit_price: float + subtotal: float } class Product { + product_id: str + name: str + price: float + stock: int + reduce_stock(quantity: int): bool } class OrderService { + notification: NotificationService + create_order(order: Order): Optional[Order] + pay_order(order: Order, payment: Payment): bool } class NotificationService { + send_email(to: str, content: str): bool } AliPayment --|> Payment : 继承 WeChatPayment --|> Payment : 继承 Order *-- OrderItem : 组合 OrderItem --> Product : 依赖 OrderService --> NotificationService : 依赖 OrderService ..> Order : 依赖 OrderService ..> Payment : 依赖 @enduml渲染后能看到:Payment是抽象类,两个支付实现类用实线三角箭头指向它;Order和OrderItem是实心菱形组合;OrderService对NotificationService、Order、Payment都是虚线依赖。关系类型基本正确,唯一需要人工确认的是OrderItem对Product的依赖——严格说应该是聚合,因为Product可以独立存在。这种边界情况在提示词里加一句"商品可独立存在时用聚合"就能修正。
4.3 生成的时序图
针对"用户下单并支付"流程,Codex 输出的时序图:
@startuml title 用户下单支付 - 时序图 autonumber actor "用户" as User participant "购物车" as Cart participant "订单服务" as OrderSvc participant "订单" as Order participant "通知服务" as Notify participant "支付接口" as Payment User -> Cart : 添加商品 activate Cart Cart --> User : 添加成功 deactivate Cart User -> Cart : 确认下单 activate Cart Cart -> OrderSvc : 创建订单 activate OrderSvc OrderSvc -> Order : 生成订单号 activate Order Order --> OrderSvc : 订单信息 deactivate Order OrderSvc -> Notify : 发送下单通知 activate Notify Notify --> OrderSvc : 通知已发送 deactivate Notify OrderSvc --> Cart : 订单创建成功 deactivate OrderSvc Cart --> User : 返回订单信息 deactivate Cart User -> OrderSvc : 选择支付方式 activate OrderSvc OrderSvc -> Payment : 调用支付接口 activate Payment Payment --> OrderSvc : 支付成功 deactivate Payment OrderSvc -> Notify : 发送支付成功通知 activate Notify Notify --> OrderSvc : 通知已发送 deactivate Notify OrderSvc --> User : 返回支付结果 deactivate OrderSvc @enduml渲染出来是一条完整的调用链,autonumber自动编号,激活框清晰标出了每个参与者的活跃区间。这里有个细节值得注意:Codex 把"订单服务"和"订单"拆成了两个参与者,这在架构上是对的——服务是业务逻辑层,订单是领域对象。如果你希望合并,提示词里说明"订单作为订单服务的内部对象,不单独作为参与者"即可。
4.4 支付回调场景的时序图
支付回调是电商系统里最容易出问题的环节,时序图能帮团队对齐"谁在什么时候做什么"。提示词:
请为"支付回调处理"生成 PlantUML 时序图: - 支付平台异步回调订单服务 - 订单服务验证签名 - 验证通过后更新订单状态为已支付 - 订单服务发送支付成功通知 - 订单服务返回成功响应给支付平台 - 如果验证失败,记录日志并返回失败 要求:用 alt 分支表示验证成功/失败,注释用中文。Codex 返回的片段:
@startuml title 支付回调处理 - 时序图 autonumber participant "支付平台" as PayPlatform participant "订单服务" as OrderSvc participant "订单" as Order participant "通知服务" as Notify PayPlatform -> OrderSvc : 异步回调通知 activate OrderSvc alt 签名验证通过 OrderSvc -> Order : 更新状态为已支付 activate Order Order --> OrderSvc : 更新成功 deactivate Order OrderSvc -> Notify : 发送支付成功通知 activate Notify Notify --> OrderSvc : 通知已发送 deactivate Notify OrderSvc --> PayPlatform : 返回成功 else 签名验证失败 OrderSvc -> OrderSvc : 记录异常日志 OrderSvc --> PayPlatform : 返回失败 end deactivate OrderSvc @endumlalt分支把成功和失败两条路径都画出来了,这对排查线上问题特别有用。实测下来,Codex 对alt、opt、loop这些组合片段的语法掌握得不错,只要提示词里说清楚分支条件。
4.5 渲染与校验
把生成的.puml文件保存到output/,跑渲染命令:
plantuml -tpng output/*.puml -o output/images/打开output/images/class_diagram.png,逐项校验:
| 校验项 | 预期 | 实际 |
|---|---|---|
| 抽象类标记 | Payment 带 abstract | 正确 |
| 继承箭头 | 实线三角 | 正确 |
| 组合箭头 | 实心菱形 | 正确 |
| 依赖箭头 | 虚线箭头 | 正确 |
| 可见性符号 | + - # | 正确 |
| 中文注释 | 无乱码 | 正确 |
如果发现关系类型不对,回到提示词里补充约束,重新生成即可。整个流程从代码到图片,熟练后五分钟内能跑完。
5. 本篇常见错排查:401、local proxy failed、OAuth 报错怎么解
配置和生成过程中,最容易卡在几个固定报错上。这一节按真实错误信息对照排查。
5.1 401 Unauthorized
报错原文:
Error: 401 Unauthorized {"error": {"message": "Invalid API key", "type": "invalid_request_error"}}原因通常是 Key 没读到或写错了。排查顺序:
第一,确认环境变量导出成功:
echo $TAOTOKEN_API_KEY如果输出为空,说明export没生效,或者你在新终端里没重新导出。把export写进~/.bashrc或~/.zshrc里持久化。
第二,确认配置文件里的env_key字段和环境变量名一致。config.toml里写的是env_key = "TAOTOKEN_API_KEY",环境变量就必须叫这个名字,大小写敏感。
第三,确认 Key 没有多余空格。从网页复制时容易带上换行,用echo $TAOTOKEN_API_KEY | wc -c看长度是否合理。
5.2 local proxy failed
报错原文:
Error: local proxy failed: dial tcp 127.0.0.1:7890: connect: connection refused这个报错说明 Codex 尝试走本地代理端口,但那个端口没有服务在监听。常见于之前配过代理工具、后来关掉了但环境变量还留着。排查:
env | grep -i proxy如果有HTTP_PROXY、HTTPS_PROXY、ALL_PROXY指向127.0.0.1:xxxx,把它们清掉:
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY然后重新跑 Codex 命令。TaoToken 的 API 地址是直连的,不需要额外代理层。
5.3 reading choices 报错
报错原文:
Error: reading choices: unexpected end of JSON input这个通常出现在流式响应被截断时。原因可能是网络抖动,或者模型返回的内容超过了单次响应限制。排查:
第一,检查网络是否稳定,重试一次。
第二,如果生成的是超长 PlantUML(比如几百个类的系统),把任务拆小,按模块分批生成。
第三,确认wire_api配置正确。config.toml里wire_api = "chat"对应 Chat Completions 格式,如果服务端要求 Responses 格式,改成wire_api = "responses"。
5.4 OAuth 相关报错
报错原文:
Error: OAuth token expired, please re-authenticate如果你用的是 Claude Code 或 Codex 的 OAuth 登录方式,token 过期后会报这个。解决方式是重新走一遍认证流程,或者改用 API Key 方式。用 TaoToken 的 Key 接入时,不需要 OAuth,直接配base_url和api_key就行,反而少了一层过期问题。
5.5 PlantUML 渲染报错
报错原文:
Error line 12: Syntax error: unexpected token这是.puml文件语法错误,通常是参与者名字带空格或特殊字符。检查报错行号对应的内容,把participant "订单 服务"改成participant "订单服务"或participant "订单_服务"。另外,中文标题如果包含冒号,也可能被解析成语法,用引号包起来。
5.6 三件套检查清单
出现任何连接类报错,先对照这张表:
| 检查项 | 正确值 | 常见错误 |
|---|---|---|
| Base URL | https://taotoken.net/api | 多了斜杠或少了 /api |
| API Key | sk-开头完整字符串 | 复制时截断或带空格 |
| Model ID | claude-sonnet-4-20250514 | 拼写错误或用了不存在的模型 |
| 环境变量名 | TAOTOKEN_API_KEY | 与 config.toml 不一致 |
| 代理变量 | 全部 unset | 残留 127.0.0.1 代理 |
把这三件套对齐,九成连接问题都能解决。排障相关的入口放在 API Keys 和接入文档:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys 、https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc 。
6. 语义一致 CTA:把 UML 生成接进你的日常流程
配置跑通、报错排完,接下来是怎么让这套流程真正省时间。我的做法是把 Codex 生成 UML 嵌进三个节点:需求评审后、代码合并前、版本发布时。
需求评审后,把领域模型代码丢给 Codex 生成类图,评审时对着图讨论,比看代码快得多。代码合并前,用 pre-commit 钩子检查.puml是否和代码同步,不同步就阻止提交。版本发布时,CI 自动重新渲染所有图,归档到文档目录。
如果你主要做模型对话调试,想先试试生成效果,可以从模型对话入口进:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat 。长期做编码和 Agent 任务,Coding Plan 的额度更合适:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan 。Claude Code 用户接入 Anthropic 通道的配置参考:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude-code-anthropic 。
最后留一个实用技巧:把常用的提示词模板存成.codex/prompts/class-diagram.md和sequence-diagram.md,每次用的时候直接引用文件路径,不用重复粘贴。Codex 支持从文件读提示词,这样团队里每个人都能用同一套模板,生成的图风格一致,评审时少很多"这个箭头什么意思"的来回。
代码一改,图自动更新,这件事从"有空再补"变成"顺手就做"。架构师的效率提升,往往就藏在这种把重复劳动交给工具的小决策里。