Don't repeat yourself,让 AI 高效、准确的干活 - (二)根据接口字段注释生成前端枚举代码

321 阅读4分钟

image.png

将上图内容转为下面的代码,大家会怎么处理呢,手搓?

export const feeItemsOptions = [
  { value: 1, label: "物业费" },
  { value: 2, label: "供暖费" },
  { value: 3, label: "水费" },
  { value: 4, label: "电费" },
  { value: 5, label: "燃气费" },
  { value: 6, label: "有线电视费" },
  { value: 7, label: "网络通讯费" },
  { value: 8, label: "车位管理费" },
  { value: 9, label: "其他" },
];

export const feeItemsMap = feeItemsOptions.reduce((obj, { label, value }) => {
  obj[value] = label;
  return obj;
}, {});

ChatGPT 没出来之前,我们确实只能选择手搓。有了 ChatGPT 之后,我们可以让 ChatGPT 帮我们生成对象数组数据,prompt 可以是如下内容:

1物业费 2供暖费 3水费 4电费 5燃气费 6有线电视费 7网络通讯费 8车位管理费 9~其他 
将上述文本转成 value、label 字段的对象数组,value 字段为整数类型,返回 json 格式数据

ChatGPT 返回内容如下:

image.png

这个结果已经符合我们的预期,只需要将生成的 json 复制粘贴就行了,已经极大的增加了摸鱼时间。

所以现在我们要完成文章开头所要求的代码,步骤是这样的,写好变量名:

export const xxxOptions = []

export const xxxMap = xxxOptions.reduce((obj, { label, value }) => {
  obj[value] = label;
  return obj;
}, {});

这部分代码,更多的时候是复制粘贴以前写好的代码

然后是复制接口文档的内容,粘贴到 ChatGPT 输入框中,将 prompt 补充完整,按下回车。复制 ChatGPT 返回的 json 粘贴到代码中。

整个流程下来,充满了复制粘贴的步骤,重复几次之后大部分人都会觉得无聊吧。所以在 ChatGPT API 开放调用的时候,我第一时间就以 vscode 插件的形式接入了,那已经是一年以前的事情了,感兴趣的可以查看我之前的文章。实现效果如下

utools-5.gif

可以看到,生成代码的过程中,选择了一个叫做生成 value-label 格式 JSON 的菜单,这是我在 vscode 插件里实现的管理 prompt 模板(或者叫代码片段也行)的方式,我之前的文章中也有专门介绍过。

最终的 prompt 是经过优化的,之前的 prompt ,ChatGPT 会在响应中添加无关紧要的内容,如下:

image.png

我们需要的只是 json 数据,当然你可以通过编码的形式将 json 部分内容匹配出来。

一种更加优秀的方式是通过 TS 类型去强化 ChatGPT 的应答质量,这个方案来自 TypeChat, 我之前的文章也有介绍过,最终的 prompt 如下:

You are a service that translates user requests into JSON objects of type "IOption" according to the following TypeScript definitions: 
'''
export type IOption = { value: string; label: string }[]; 
'''
The following is a user request: 
'''
1-电费,2-水费,3-煤气费,4-电视费,5-电话费,6-上网费,7-车位费,8-供暖费,9-物业费,10-其它
'''
The following is the user request translated into a JSON object with 2 spaces of indentation and no properties with the value undefined:

生成结果如下,只有 json 数据,没有多余的废话:

image.png

上述功能去年的时候就已经在 vscode 插件里实现了,也写文章简单介绍过,为什么现在还要写这篇文章呢?

因为我在 uTools 插件里又实现了一遍。主要还是因为 vscode 插件的适用场景有限,只能在 vscode 里使用,而 uTools 插件几乎能在所有场景下做到随用随开,用完就关。以生成上面代码为例,效果如下:

utools-6.gif

uTools 插件也有不足的地方,就是无法获取到当前选中的文本,所以需要在生成代码之前手动输入变量名,而 vscode 插件中是先打出变量名然后选中就行。

uTools 中管理 prompt 模板(代码片段),我没有去重复造轮子,而是使用 uTools 的官方插件 “自动化脚本” 。在脚本的管理上做了功夫,比如以 TypeScript 编写脚本,实现脚本自动导入及更新。下面是本文例子中的脚本代码:

import path from 'path';
import fs from 'fs';
import { clipboard } from 'electron';
import { validate } from '@share/TypeChatSlim/utools';
import { compile as compileEjs } from '@share/utils/ejs';
import {
  askChatGPT as askOpenai,
  getBlockConfigPath,
  getBlockJsonValidSchema,
} from '@share/utils/uTools';

export const bootstrap = async (scriptFile?: string) => {
  const schema = getBlockJsonValidSchema(scriptFile!);
  const clipboardText =
    (clipboard.readText() || '').trim() ||
    '客户验收状态:1.无需验收、2.待验收、3已验收';
  const typeName = 'IOption';
  const requestPrompt =
    `You are a service that translates user requests into JSON objects of type "${typeName}" according to the following TypeScript definitions:\n` +
    `\`\`\`\n${schema}\`\`\`\n` +
    `The following is a user request:\n` +
    `"""\n${clipboardText}\n"""\n` +
    `The following is the user request translated into a JSON object with 2 spaces of indentation and no properties with the value undefined:\n`;
  utools.redirect(['lowcode', 'lowcode'], {
    type: 'text',
    data: JSON.stringify({
      scriptFile,
      route: '/chat',
      content: requestPrompt,
    }),
  });
};

type LLMMessage = (
  | {
      role: 'system';
      content: string;
    }
  | {
      role: 'user';
      content:
        | string
        | (
            | {
                type: 'image_url';
                image_url: { url: string };
              }
            | { type: 'text'; text: string }
          )[];
    }
)[];

// 给页面调用的
export const askChatGPT = async (data: {
  messages: LLMMessage;
  handleChunk: (chunck: string) => void;
  scriptFile: string;
}) => {
  const configPath = getBlockConfigPath(data.scriptFile);
  const schema = getBlockJsonValidSchema(data.scriptFile);
  const template = fs.readFileSync(
    path.join(configPath, 'template.ejs'),
    'utf8',
  );
  const typeName = 'IOption';

  if (
    data.messages.length >= 3 &&
    (data.messages[data.messages.length - 1].content as string).includes('>>>')
  ) {
    const name = (data.messages[data.messages.length - 1].content as string)
      .split('>>>')[1]
      .trim();
    const jsonValid = validate(
      data.messages[data.messages.length - 2].content as string,
      schema,
      typeName,
    );
    if (jsonValid.success) {
      setTimeout(() => {
        const code = compileEjs(template, {
          rawSelectedText: name || '请手动修改名称',
          content: JSON.stringify(jsonValid.data),
        } as any);
        clipboard.writeText(code);
        utools.outPlugin();
        utools.hideMainWindowPasteText(code);
      }, 300);
    } else {
      data.handleChunk(`

> 生成代码时 JSON 校验不通过

${jsonValid.message}
						`);
    }
    return '';
  }
  const content = await askOpenai({
    messages: data.messages,
    handleChunk: data.handleChunk,
  });
  const valid = validate(content.content, schema, typeName);
  if (valid.success) {
    data.handleChunk(`

JSON 校验通过,输入\`>>>\`生成代码
			`);
  } else {
    data.handleChunk(`

> JSON 校验不通过

${valid.message}
						`);
  }
  return content;
};

uTools 中选中命令后执行 bootstrap 方法,内部逻辑是获取剪贴板中的内容,拼接好 prompt, 调用另一个 uTools 插件,并且将 prompt 传过去。

被调起的 uTools 插件其实就是一个支持 LLM 会话的网页,接收到 prompt 后自动调用askChatGPT 方法。

不管是 vscode 插件还是 uTools 插件,都有好的地方和不足之处,所以工作中两者结合着使用更加事半功倍。

vscode 插件对应 prompt 模板代码

uTools 插件对应 prompt 模板代码