技术框架

用前端技术栈中的D3.js、JavaScript及SVG图形处理,并结合大模型接口,用户可通过聊天窗口与大模型进行交互,实现对图形的操作与更新。

模型调用

可以选择任何一家平台的大模型API接口。此处推荐大家一个平台,https://platform.deepseek.com/usage,该平台可以调用deepseek大模型,新用户注册有免费500tokens的额度。

调用方法如下:(调用方法有多种,可以参考官方文档首次调用 API | DeepSeek API Docs,下面给我我在项目中的调用方法)

      const apiKey = "apiKey";
      const response = await fetch('<https://api.deepseek.com/chat/completions>', {
          method: 'POST',
          headers: {
              'Content-Type': 'application/json',
              'Authorization': `Bearer ${apiKey}`
          },
          body: JSON.stringify({
              model: 'deepseek-chat',
              messages: [{ role: "user", content: prompt }],
              temperature: 0.3,
              max_tokens: 1000
          })
    });
        const data = await response.json();
        let modifiedSVGCode = data.message.content;

利用模型操作图形

大模型的返回结果往往为大量文本和代码的集合,我们通过简单的调用得到的只是大模型的全部应答,若要实现对图形的操作,需要对大模型的返回结果进行清洗,例如实验中需要实现对图形进行变换操作,我们只需要将选中图形的SVG代码传给大模型,通过获取大模型回复的代码输出对原有画布中的图形进行操作。

提供一种操作逻辑流程:通过解析大模型返回的SVG代码,在原有画布中将原有模型SVG代码进行替换即可做到利用大模型进行操作的效果。

下面为对大模型返回代码进行操作的示例:

        // 提取SVG代码(如果返回的是代码块格式)
        if (content.includes('```')) {
            modifiedSVGCode = content.split('```')[1]
                .replace(/^xml\\n|^svg\\n/i, '')
                .trim();
        }

        // 验证SVG代码
        if (!modifiedSVGCode.includes('<') || !modifiedSVGCode.includes('>')) {
            throw new Error('返回的内容不包含有效的SVG代码');
        }

        // 更新画布中的图形
        const parser = new DOMParser();
        const doc = parser.parseFromString(modifiedSVGCode, "image/svg+xml");
        const newElement = doc.documentElement;

        // 保存原始的变换信息和属性
        const originalTransform = selectedShape.attr("transform");
        const originalScale = selectedShape.attr("data-scale");
        const originalType = selectedShape.attr("data-type");
        const originalId = selectedShape.attr("data-id");

        // 保存原有图形的引用和数据
        const oldElements = selectedShape.selectAll("*:not(.resize-area)").nodes();
        const oldResizeArea = selectedShape.select(".resize-area");
        const oldResizeAreaData = oldResizeArea.empty() ? null : {
            x: oldResizeArea.attr("x"),
            y: oldResizeArea.attr("y"),
            width: oldResizeArea.attr("width"),
            height: oldResizeArea.attr("height")
        };

        // 创建新的组来存放过渡元素
        const transitionGroup = selectedShape.append("g")
            .attr("class", "transition-group")
            .style("opacity", 1);

        // 将原有元素克隆到过渡组
        oldElements.forEach(node => {
            transitionGroup.node().appendChild(node.cloneNode(true));
        });

        // 清除原有内容(但保留过渡组)
        selectedShape.selectAll("*:not(.transition-group)").remove();

        // 添加新的图形元素
        Array.from(newElement.children).forEach(child => {
            if (child.tagName !== 'defs') {
                selectedShape.node().appendChild(child.cloneNode(true));
            }
        });