模型生成的Mermaid语法修复
本文探讨提升大模型生成Mermaid语法图的准确率。通过两个优化方向:1)优化提示词降低初次生成错误率(从12%降至5%);2)开发实时校验修复方案。重点介绍使用Mermaid官方API和JSDOM构建Node环境校验器,解决DOMPurify兼容性问题,最终通过补丁修改实现100%修复成功率。测试数据显示初始错误率4%,经修复后全部成功,为Mermaid图生成提供了可靠解决方案。
TL;DR
1. 本文记录一下大模型应用通用的优化思路。
2. 顺带目标:让LLM生成的mermaid内容实现100%语法正确率。
背景
在大模型生成的过程中,为了丰富输出模态,我们常常会让其生成mermaid语法的示意图,然后基于mermaid进行渲染。
然而,现实渲染出来的效果存在较大的概率会渲染失败(1/6)
在我测试中,即便是腾讯元宝(DS版)都存在生成的mermaid图无法渲染的情况。
(本质是语法错误。)
基于此,我们考虑自己提升一下生成的mermaid语法准确率。
思路
在大模型生成的内容中基于正则提取mermaid内容之后(如果是流式输出可以考虑流式累积劫持),我们可以进行
我们从以下两个思路入手:
1. 降低LLM初次生成的错误率;
2. 检测LLM生成之后的错误,并且实现修复。
针对第一点,从模型层面无非是针对模型知识更新、微调;
但是从工作流角度,还是提示词工程来得最快。从司内构建的工作流结果来看,大致可以从12%的错误率可以降低到5%。这点根据错误信息逆向提示词限制即可。
比如如果模型生成的mermaid语法里面经常出现xx标点符号导致语法失败,则提示不要使用xx标点符号或者使用yy代替xx即可。
本文重点说下第二点,如何检测到生成的错误并且实施修复。
方案
第一步就得需要判断生成的mermaid是否正确。
难点:但是在判断的过程中,通常只有到了前端进行渲染的时候才能知道结果,而node等服务器端生成的时候其实是不知道的。
虽然官方未提供独立的语法检查库,但通过以下方法可实现实时校验:
1. 🔧 调用Mermaid官方API
原理:利用mermaid
包的parse()
方法执行语法预检
步骤:
-
安装依赖:
npm install mermaid @types/mermaid
-
创建校验函数:
import mermaid from 'mermaid'; // 异步初始化(v10+需此步骤) await mermaid.initialize({ startOnLoad: false }); /** 语法校验函数 */ const validateMermaid = async (code: string): { valid: boolean; error?: string } => { try { await mermaid.parse(code); return { valid: true }; } catch (err) { return { valid: false, error: (err as Error).message.replace(/^Parse error/, '') // 清理错误信息 }; } }; // 测试用例 const result = await validateMermaid(` graph TD A[Start] --> B{Decision} B -->|Yes| C[End] `); console.log(result); // { valid: true }
优势:
✅ 使用官方解析引擎,与渲染器行为一致
✅ 精确捕获Syntax error in text
等位置信息
问题1:Node 环境 - DOMPurify.addHook is not a function
node环境下会抛错. DOMPurify.addHook is not a function
:
可能原因:
-
DOMPurify 版本升级:Mermaid 11.8.1 强制依赖特定版本的 DOMPurify,但在 Node.js 环境中存在兼容问题
-
浏览器 API 依赖:
addHook
是浏览器环境特定 API,在 Node.js 环境下无法使用
TypeError: DOMPurify.addHook is not a function
at setupDompurifyHooks (/Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-3XYRH5AP.mjs:2685:13)
at <anonymous> (/Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-3XYRH5AP.mjs:2678:7)
at removeScript (/Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-3XYRH5AP.mjs:2702:3)
at sanitizeMore (/Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-3XYRH5AP.mjs:2710:14)
at Object.sanitizeText (/Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-3XYRH5AP.mjs:2727:31)
at FlowDB.sanitizeText (file:///Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/flowDiagram-PKI6S5ZS.mjs:99:27)
at FlowDB.addVertex (file:///Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/flowDiagram-PKI6S5ZS.mjs:157:18)
at Object.anonymous (file:///Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/flowDiagram-PKI6S5ZS.mjs:1176:14)
at Parser.parse (file:///Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/flowDiagram-PKI6S5ZS.mjs:1560:36)
at newParser.parse (file:///Users/....../node_modules/.pnpm/mermaid@11.8.1/node_modules/mermaid/dist/chunks/mermaid.core/flowDiagram-PKI6S5ZS.mjs:2316:23)
Node.js v18.20.8
方法:使用 JSDOM 创建沙盒环境
追加一段
pnpm install jsdom
代码:
import { JSDOM } from 'jsdom';
// 创建虚拟DOM环境
const dom = new JSDOM();
// 复制浏览器全局对象
Object.assign(global, {
window: dom.window,
document: dom.window.document,
});
完整代码
// npm install mermaid @types/mermaid
import { JSDOM } from 'jsdom';
import mermaid from 'mermaid';
// 创建虚拟DOM环境
const dom = new JSDOM();
// 复制浏览器全局对象
Object.assign(global, {
window: dom.window,
document: dom.window.document,
});
// 异步初始化(v10+需此步骤)
async function initMermaid() {
await mermaid.initialize({ startOnLoad: false });
const validateMermaid = async (code: string) => {
try {
// 版本适配处理
const parser = mermaid.parse;
if (typeof parser === 'function') {
await parser(code.trim());
return { valid: true };
}
throw new Error('Mermaid parser unavailable');
} catch (err) {
return {
valid: false,
error: (err as Error).message
.replace(/^Syntax error in text/, '')
.replace(/Line:\s*\d+,?\s*/, ''),
};
}
};
// 测试用例
const result = await validateMermaid(`
graph TD
A[Start] --> B{Decision}
B -->|Yes| C[End]
`);
console.log(result); // { valid: true }
console.log('111');
const result2 = await validateMermaid(`
journey
title 用户参与路径
section 回归勇士(低活跃)
登录弹窗 --> 领取疲劳药 --> 单刷副本: “免费补资源缺口!”
section 冒险新兵(潜力)
组队刷图 --> 积累积分 --> 兑换强化券: “3天战力追上老玩家!”
section 公会战神(核心)
邀3好友 --> 加速解锁奖池 --> 瓜分天空套: “带队白嫖顶级皮肤!”
`);
console.log(result2);
console.log('222');
}
void initMermaid();
输出如下:
问题2:node bin 执行打包后的代码失败
构建有两大坑,一个是高版本的 mermaid只能以esm方式构建,不支持cjs的方式。
解决方案:判断是否存在default属性切换或者版本降级。
还有一个大坑是出现如下内容:
TypeError: eI.sanitize is not a function
at Object.up [as sanitizeText] (/Users/..../node_modules/.pnpm/mermaid@9.4.0/node_modules/mermaid/dist/mermaid.min.js:17:930)
at RV (/Users/..../node_modules/.pnpm/mermaid@9.4.0/node_modules/mermaid/dist/mermaid.min.js:1236:11294)
......
at validateMermaid (/Users/..../packages/agent-http-server/bin/utils/mermaid.js:53:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Timeout._onTimeout (/Users/..../packages/agent-http-server/bin/utils/mermaid.js:267:5)
{ valid: false, error: 'eI.sanitize is not a function' }
高版本:
解决方法:需要在mermaid构建的代码内容做修改,使用一个同构的dompurify来解决
所以还是需要修改npm包。
所以我们进行最终的操作:
2. pnpm patch 对mermaid依赖打补丁
最终基于pnpm patch了对应的mermaid包内部代码,将仅支持在浏览器端使用的dompurify为isomorphic-dompurify依赖。(具体操作可以自行搜索“pnpm patch”,此处不累赘。)
结果
最后不要忘记可以给你的这段代码主动记录修复日志,基于最大重试次数可以实现如下效果:
总修复尝试次数: 27
修复成功数量: 27
修复失败数量: 0
默认成功数量: 26
修复成功数量: 1
默认生成错误率: 4%
修复成功率: 100.00%
更多推荐
所有评论(0)