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()方法执行语法预检

​步骤​​:

  1. 安装依赖:

    	npm install mermaid @types/mermaid 
  2. 创建校验函数:

    	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 :

可能原因:

  1. ​DOMPurify 版本升级​​:Mermaid 11.8.1 强制依赖特定版本的 DOMPurify,但在 Node.js 环境中存在兼容问题

  2. ​浏览器 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%

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐