一个面向二维图形生成的三阶段 LangGraph Agent:
- 自然语言需求先转换为可转换的 SVG 子集
- 再把 SVG 解析为结构化
geometry_plan - 最后基于
SVG + geometry_plan + 转换提示生成受约束的 AutoLISP
项目目标不是“直接让模型随便写 Lisp”,而是通过确定性的几何中间层、严格的 SVG 子集和保真校验,把输出收敛到更稳定、更可落地的范围内。
- 三阶段生成:
自然语言 -> SVG -> geometry_plan -> AutoLISP - 简短需求自动扩写:对过短、过泛的描述先补充成更适合绘制的请求
- SVG 双校验:同时校验安全性和可转换性,确保后续几何解析可稳定执行
- 确定性几何解析:把已验证 SVG 归一化为结构化
geometry_plan - LISP 强保真校验:除了语法/安全约束,还会校验几何是否被保留
- 自动重试:SVG 阶段失败最多重试 2 次,LISP 阶段失败最多重试 3 次
- 结果持久化:自动保存输入、增强后的需求、SVG、
geometry_plan.json、LISP 产物 - 接口保持稳定:CLI 入口、工作流调用方式、实体/命令白名单不变
flowchart LR
A["用户请求"] --> B["增强需求(仅在请求过短时触发)"]
B --> C["生成 SVG"]
C --> D["校验 SVG(安全 + 可转换性)"]
D -->|失败,最多 2 次重试| C
D -->|通过| E["构建 geometry_plan"]
E --> F["生成 AutoLISP"]
F --> G["校验 AutoLISP(语法/安全 + 几何保真)"]
G -->|失败,最多 3 次重试| F
G -->|通过| H["保存产物"]
- Python
>= 3.12 - 一个兼容 OpenAI Chat Completions 的模型服务
- 建议安装开发依赖以便运行测试和 LangGraph Studio
如果你使用 venv:
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"如果你已经有自己的 Python 3.12 环境,也可以直接执行:
pip install -e ".[dev]"项目会从 .env 和当前 shell 环境中读取模型配置。
最少需要:
OPENAI_API_KEY=your_api_key常用完整配置如下:
OPENAI_API_KEY=your_api_key
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-mini说明:
OPENAI_BASE_URL未设置时,默认使用https://api.openai.com/v1OPENAI_MODEL未设置时,默认使用gpt-4o-mini- 代码也兼容
OPENAI_API_BASE和ENGINEERING_MODEL_NAME这两个备用变量名
命令行执行一次完整流程:
python -m agent.cli "画一个台灯" --out ./outputs/lamp运行完成后,终端会输出最终生成的 AutoLISP 脚本,同时在 --out 指定目录下写入相关文件。
每次运行会保存以下产物:
input.txt:原始用户请求effective_request.txt:增强后的有效请求result.svg:中间 SVG 结果geometry_plan.json:确定性解析得到的几何计划result.lsp:最终 AutoLISP 脚本
返回状态中的 artifact_paths 也会包含:
inputeffective_requestsvggeometry_planlisp
仓库里已经包含一个示例输出目录:outputs/lamp/
geometry_plan 是本次升级的核心中间层。它不是原始 SVG 的简单转存,而是把 SVG 归一化后的结构化几何信息,供 LISP 阶段和保真校验共同使用。
当前 geometry_plan 至少包含:
- 图元顺序
- 轮廓类型
- 是否闭合
- 主轮廓 / 孔洞 / 细节角色
- 圆、直线和矩形的直接几何信息
- path 的分段结果
- 曲线段的圆弧候选或自适应分段点列
- 建议优先使用的 AutoLISP 实体类型
- 用于保真校验的最低保留要求
如果你想直接在 Python 中调用 LangGraph:
import asyncio
from agent.graph import graph
result = asyncio.run(
graph.ainvoke(
{
"user_request": "画一个带底座和圆润灯头的台灯正视图",
"output_dir": "./outputs/lamp",
}
)
)
print(result["svg_text"])
print(result["geometry_plan"])
print(result["lisp_text"])返回状态中会包含:
effective_requestrequest_was_enhancedsvg_textgeometry_planlisp_textartifact_paths- 各阶段重试计数与校验错误信息
仅允许以下标签:
linepolylinepolygoncirclerectpath
另外还要求:
- 根节点必须是
<svg> - 不允许使用
transform - 不允许使用
href或xlink:href引用外部资源 - 不允许依赖
fill-rule/clip-rule表达轮廓关系 path只允许绝对大写命令:M、L、C、A、Z- 一个
path只允许表示一个轮廓,不能包含多个子路径 rect仅用于真矩形;圆角矩形应改用path
circle、line、rect、polyline、polygon做直接映射path会做命令解析、绝对坐标归一化和分段- 可稳定识别为圆弧的段,会给出
ARC或LWPOLYLINE bulge候选提示 - 不能稳定识别为圆弧的曲线,会按误差阈值做自适应分段,输出高质量折线点列提示
生成结果必须满足:
- 第一层必须是
(progn ...) - 必须包含
(getpoint "请选择放置点: ") - 至少包含一个
entmake - 不能包含 Markdown 代码块
同时会拒绝使用以下类别的能力:
commandforeachvlax*- 文件读写
- DCL 对话框
setvar/getvar
除白名单函数外,只允许调用脚本内部通过 defun 定义的辅助函数。
在原有语法/安全校验之外,还会增加几何保真校验。当前首版包含这些硬规则:
- SVG 中存在
circle,LISP 必须生成对应数量的CIRCLE - 如果主轮廓或显著细节轮廓包含曲线段,LISP 必须生成
ARC或至少一个带非零42bulge 的LWPOLYLINE - 如果源 SVG 没有
rect,LISP 不允许用RECT取代主轮廓 entmake数量不能低于geometry_plan中显著图元的最低数量要求- 多轮廓结构不能退化成单轮廓,孔洞和独立细节必须保留为独立实体
如果校验失败,系统会把错误原因附加到下一次提示词中并自动重试。
.
├── src/agent/
│ ├── cli.py # CLI 入口
│ ├── geometry.py # SVG -> geometry_plan 的确定性几何解析
│ ├── graph.py # LangGraph 工作流定义
│ ├── nodes.py # 各节点实现
│ ├── llm.py # 模型配置与构造
│ ├── prompts.py # Prompt 加载与渲染
│ ├── state.py # 图状态定义
│ └── validators.py # SVG / AutoLISP 校验
├── prompts/ # 各阶段提示词模板
├── tests/ # 单元测试
├── outputs/ # 生成结果示例
├── langgraph.json # LangGraph Studio 配置
└── pyproject.toml
当前测试覆盖重点包括:
- SVG 校验测试
geometry_plan解析测试- LISP 语法/安全校验测试
- LISP 几何保真测试
- 图流程集成测试
运行测试:
pytest启动 LangGraph Studio 本地调试:
langgraph dev --config langgraph.json --no-browserprompts/ 目录下包含 3 组提示词:
brief_enhance_*:把过短需求扩写成更适合出图的描述svg_*:生成受限 SVG 子集lisp_*:把SVG + geometry_plan转为 AutoLISP
如果你想调整生成风格、约束强度或输出格式,这里仍然是最直接的切入点。
- 当前只支持 CAD 友好的 SVG 子集,不追求完整 SVG 规范兼容
path只支持绝对大写M/L/C/A/Z- 保真校验只关注“几何是否被保留”,不涉及材质、风格、阴影等非几何审美判断
- 对复杂、多视图、强参数化图形,仍可能需要继续扩展几何计划和转换策略
仓库目前未声明许可证;如需对外发布,建议补充 LICENSE 文件。