我的2023年度关键词:ChatGPT、生产力工具
2023 是 AI 大爆發的一年,這一年我在我的生產力工具中(一個叫 lowcode 的 vscode 插件)接入了 ChatGPT API,插件也進行了重構,日常搬磚也因為 ChatGPT 的引入發生了很大的變化。
在介紹 ChatGPT 是如何與 lowcode 插件結合之前,先說說 lowcode 插件的發展歷史,畢竟從 2020 年第一個版本發布到現在也迭代 3 年多了。
介紹
*的產生
一開始寫這么個插件的目的為了拉取 YAPI 接口文檔信息生成前端 API 請求方法,如下
export interface IFetchUserListResult {
code: number;
msg: string;
result: {
rows: {
name: string;
age: number;
mobile: string;
address: string;
tags: string[];
id: number;
}[];
total: number;
};
}
export interface IFetchUserListParams {
name?: string;
page: number;
size: number;
}
/**
* 用戶列表
* http://yapi.smart-xwork.cn/project/129987/interface/api/1796953
* @author 劃水摸魚糊屎工程師
*
* @param {IFetchUserListParams} params
* @returns
*/
export function fetchUserList(params: IFetchUserListParams) {
return request<IFetchUserListResult>(`${env.API_HOST}/api/user/page`, {
method: 'GET',
params,
});
}
之后增加了根據 JSON 生成 API 請求、根據 JSON 生成 TS 類型等功能。
物料的概念
再之后就是引入了物料的概念:代碼片段和區塊。上面說的根據 YAPI 接口信息生成 API 請求方法、根據 JSON 生成 API 請求、根據 JSON 生成 TS 類型都屬于代碼片段,只在當前激活的文件里生成代碼。而區塊就是在多個文件里生成代碼(或者說創建多個文件)。
代碼片段
代碼片段可以通過右鍵菜單、輸入提示、可視化界面進行使用,區塊只能通過可視化界面使用。
右鍵菜單
輸入提示
輸入提示類似 vscode 自帶的代碼片段功能,同時兼容 vscode 代碼片段的語法
可視化界面
代碼片段可視化的功能目前我也很少用到(可以不用,但不能沒有)
區塊
前面也說了,區塊是為了在多個文件里生成代碼(或者創建多個文件)。比如寫一個 react 組件的時候,可能包含 js 文件和 css 文件。
不同區塊的 Schema 表單不一樣,產生不一樣的模板數據,就可以達到模板數據 + 模板生成代碼的目的。
內部細節
插件讀取項目根目錄下的 materials/blocks 作為區塊,讀取 materials/snippets 作為代碼片段。
目前已經支持配置任意目錄,在所有項目*享物料
代碼片段和區塊目錄內的內容如下
主要是 src 目錄內的內容存在差異,代碼片段的 src 目錄內必須是 template.ejs 文件,區塊的 src 目錄內可以是任意內容。生成代碼的時候,使用 ejs 模板引擎編譯 ejs 文件。代碼片段是將編譯以后的內容插入到編輯器光標所在的位置,區塊是將編譯后的文件拷貝到指定的目錄里(非 ejs 文件直接拷貝)。
model.json
默認模板數據
preview.json
物料相關配置
{
"title": "",
"description": "",
"img": [
"https://gitee.com/img-host/img-host/raw/master/2020/11/05/1604587962875.jpg"
],
"category": [],
"notShowInCommand": true,
"notShowInSnippetsList": true,
"notShowInintellisense": true,
"showInRunSnippetScript": true,
"schema": "amis",
"scripts": []
}
schema.json
可視化界面 Schema 表單配置,支持 form-render、formily、amis。
script/index.js
模板編譯周期鉤子函數
module.exports = {
beforeCompile: context => {
console.log(context)
},
afterCompile: context => {
console.log(context)
},
complete: context => {
console.log(context)
},
}
重構之后會利用這個文件做更多有趣的事
缺陷
右鍵菜單使用代碼片段的適用范圍有限
export const generateCode = (context: vscode.ExtensionContext) => {
context.subscriptions.push(
vscode.commands.registerTextEditorCommand(
'lowcode.generateCode',
async () => {
const rawClipboardText = getClipboardText();
let clipboardText = rawClipboardText.trim();
clipboardText = JSON.stringify(jsonParse(clipboardText));
const validYapiId = isYapiId(clipboardText);
const validJson = jsonIsValid(clipboardText);
const valid = validJson || validYapiId;
if (valid) {
if (validYapiId) {
await genCodeByYapi(clipboardText, rawClipboardText);
} else {
await genCodeByJson(clipboardText, rawClipboardText);
}
return;
}
try {
await genCodeByTypescript(rawClipboardText, rawClipboardText);
} catch {
window.showErrorMessage('請復制Yapi接口ID或JSON字符串或TS類型');
}
},
),
);
};
代碼里寫死了邏輯,只能處理 json 、ts 類型、YAPI 接口。
與 ChatGPT 的結合
引入 ChatGPT 的最初目的是為了翻譯區塊 Schema 表單對應的模板數據里的指定字段。
翻譯物料模板數據指定字段
點擊 Ask ChatGPT 就會打開 ChatGPT 的 WebView 界面,并自動發送預設的 Prompt。
預設的 Prompt 就是放在 viewPrompt.ejs 中
內容如下:
<%- model %> 將這段 json 中,filters 字段中的 key 字段翻譯為英文,使用駝峰語法,label、placeholder
保留中文。columns 字段中的 key、dataIndex 字段翻譯為英文,使用駝峰語法,title 字段保留中文。
返回翻譯后的 markdown 語法的代碼塊
這種方式和 ChatGPT 交流會有各種玄學問題,比如翻譯的字段不對或者所有的字段都翻譯了,也可能是我寫的 Prompt 有問題,這個功能也幾乎不用了,后面會介紹另一種方式。
代碼片段當作 Prompt 管理工具
看過幾個 vscode 里 ChatGPT 的插件,大都是寫死幾個菜單,比如解釋一段代碼的意思、重構一段代碼、給代碼添加單元測試,說實話,有點 low low 的。
我是加了兩個菜單:Ask ChatGPT、Ask ChatGPT With Template
Ask ChatGPT
邏輯很簡單,直接把當前選中的代碼或者剪貼板里的內容原封不動的發給 ChatGPT。
vscode.commands.registerCommand('lowcode.askChatGPT', () => {
showChatGPTView({
task: {
task: 'askChatGPT',
data: getSelectedText() || getClipboardText(),
},
});
}),
其實這個菜單完全可以去掉,用 Ask ChatGPT With Template 也能實現
Ask ChatGPT With Template
顧名思義就是根據不同的場景使用不同的 Prompt 模版去問 ChatGPT。
只需要在代碼片段的目錄下添加 commandPrompt.ejs 文件即可
內容可能如下
下面我讓你來充當翻譯家,你的目標是把中文翻譯成英文單詞,請翻譯時使用駝峰格式,小寫字母開頭,不要帶翻譯腔,而是要翻譯得自然、流暢和地道,使用優美和高雅的表達方式。
請翻譯下面的內容:“<%- rawSelectedText || rawClipboardText %>”
重構、優化
之前提到過右鍵菜單使用代碼片段的適用范圍有限,只能處理 json 、ts 類型、YAPI 接口。接入 ChatGPT 后,又加了兩個菜單。如果之后要加什么新功能還得接著加菜單,那就太 low 了。
雖然引入了 ChatGPT,但是 ChatGPT 的交互頁面是獨立的,ChatGPT 返回結果后還需要手動復制,我這種懶人是無法接受的。
webview 界面調用 nodejs 腳本
可視化界面配置表單還是挺費時的,而且原來的 Ask ChatGPT 的功能也比較玄學。加了一個“執行腳本”的按鈕,可以實現調用物料目錄下 src/index.js 文件內指定方法。
在使用 ChatGPT 進行翻譯的時候,使用了 TypeChat(關于 TypeChat 可以看 TypeChat、JSONSchemaChat實戰 - 讓ChatGPT更聽你的話),但是并不需要在插件內部引入 TypeChat。如下
export async function handleAskChatGPT() {
const { lowcodeContext } = context;
const schema = fs.readFileSync(
path.join(lowcodeContext!.materialPath, 'config/schema.ts'),
'utf8',
);
const typeName = 'PageConfig';
const res = await translate<PageConfig>({
schema,
typeName,
request: JSON.stringify(lowcodeContext!.model as PageConfig),
completePrompt:
`你是一個根據以下 TypeScript 類型定義將用戶請求轉換為 "${typeName}" 類型的 JSON 對象的服務,并且按照字段的注釋進行處理:\n` +
`\`\`\`\n${schema}\`\`\`\n` +
`以下是用戶請求:\n` +
`"""\n${JSON.stringify(lowcodeContext!.model as PageConfig)}\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`,
createChatCompletion: lowcodeContext!.createChatCompletion,
showWebview: true,
extendValidate: (jsonObject) => ({ success: true, data: jsonObject }),
});
lowcodeContext!.outputChannel.appendLine(JSON.stringify(res, null, 2));
if (res.success) {
return { ...res.data };
}
return lowcodeContext!.model;
}
腳本方法執行完后將模版數據(model)返回,省去手動復制。
可以像寫業務代碼一樣,根據自己需要添加各種處理方法,嘗試各種新的技術。
如果 schema 表單用的是 amis,還可以在 schema 中配置執行腳本,比如:
{
"type": "button",
"label": "插入// lowcode-model-import-api",
"onEvent": {
"click": {
"actions": [{
"actionType": "runScript",
"args": {
"method": "insertPlaceholder",
"params": "http:// lowcode-model-import-api"
}
}]
}
}
}
點擊按鈕的時候,會調用 insertPlaceholder 方法,參數為 // lowcode-model-import-api
Run Snippet Script
添加了 Run Snippet Script 菜單
選擇對應的模版(代碼片段)后,會執行模版目錄下 src/index.js 的 onSelect 方法,方法里可以寫任何邏輯。
只需要將代碼片段目錄下的 config/preview.json 文件里的showInRunSnippetScript 設置為 true,代碼片段就會出現在菜單中。
{
"title": "",
"description": "",
"img": [
"https://gitee.com/img-host/img-host/raw/master/2020/11/05/1604587962875.jpg"
],
"category": [],
"notShowInCommand": false,
"notShowInSnippetsList": true,
"notShowInintellisense": true,
"showInRunSnippetScript": true,
"schema": "amis",
"scripts": []
}
這個功能的加入,可以做很多有趣的事情,如下:
axios-request-api
把插件內部根據 YAPI 接口文檔信息生成前端 API 請求方法的代碼挪到了外面,并且加了個有意思的功能,讓 ChatGPT 生成請求方法的名稱,部分代碼如下:
const res = await fetchApiDetailInfo(domain, yapiId, token);
if (!res.data.data) {
throw res.data.errmsg;
}
funcName = await context.lowcodeContext!.createChatCompletion({
messages: [
{
role: 'system',
content: `你是一個代碼專家,按照用戶傳給你的 api 接口地址,和接口請求方法,根據接口地址里的信息推測出一個生動形象的方法名稱,駝峰格式,返回方法名稱`,
},
{
role: 'user',
content: `api 地址:${res.data.data.query_path},${res.data.data.method} 方法,作用是${res.data.data.title}`,
},
],
});
typeName = `I${funcName.charAt(0).toUpperCase() + funcName.slice(1)}Result`;
完整代碼:https://github.com/lowcode-scaffold/lowcode-materials/tree/master/materials/snippets/axios-request-api-外掛腳本/script
OCR
使用百度 OCR 識別圖片文字
因為 nodejs 沒法讀取剪貼板里的圖片,只能打開一個 webview 去讀取,核心代碼如下:
import { window, Range, env } from 'vscode';
import { generalBasic } from '../../../../../share/BaiduOCR/index';
import { context } from './context';
export async function bootstrap() {
const { lowcodeContext } = context;
const clipboardImage = await lowcodeContext?.getClipboardImage();
const ocrRes = await generalBasic({ image: clipboardImage! });
const words = ocrRes.words_result.map((s) => s.words).join(',');
env.clipboard.writeText(words).then(() => {
window.showInformationMessage('內容已經復制到剪貼板');
});
window.activeTextEditor?.edit((editBuilder) => {
// editBuilder.replace(activeTextEditor.selection, content);
if (window.activeTextEditor?.selection.isEmpty) {
editBuilder.insert(window.activeTextEditor.selection.start, words);
} else {
editBuilder.replace(
new Range(
window.activeTextEditor!.selection.start,
window.activeTextEditor!.selection.end,
),
words,
);
}
});
}
啟動一個 nestjs 服務
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getMaterialPath() {
return this.appService.getMaterialPath();
}
}
import { Injectable } from '@nestjs/common';
import { context } from './context';
@Injectable()
export class AppService {
getMaterialPath() {
return context.lowcodeContext?.materialPath;
}
}
完整代碼:https://github.com/lowcode-scaffold/lowcode-materials/tree/master/materials/snippets/start nest api server/script
生成 value-label 格式 JSON
使用了TypeChat,ChatGPT 返回的結果有提問,最終重試之后正確了。
完整代碼:https://github.com/lowcode-scaffold/lowcode-materials/tree/master/materials/snippets/生成 value-label 格式 JSON/script
翻譯成駝峰格式
代碼:
import { env, window, Range } from 'vscode';
import { context } from './context';
export async function bootstrap() {
const clipboardText = await env.clipboard.readText();
const { selection, document } = window.activeTextEditor!;
const selectText = document.getText(selection).trim();
let content = await context.lowcodeContext!.createChatCompletion({
messages: [
{
role: 'system',
content: `你是一個翻譯家,你的目標是把中文翻譯成英文單詞,請翻譯時使用駝峰格式,小寫字母開頭,不要帶翻譯腔,而是要翻譯得自然、流暢和地道,使用優美和高雅的表達方式。請翻譯下面用戶輸入的內容`,
},
{
role: 'user',
content: selectText || clipboardText,
},
],
});
content = content.charAt(0).toLowerCase() + content.slice(1);
window.activeTextEditor?.edit((editBuilder) => {
if (window.activeTextEditor?.selection.isEmpty) {
editBuilder.insert(window.activeTextEditor.selection.start, content);
} else {
editBuilder.replace(
new Range(
window.activeTextEditor!.selection.start,
window.activeTextEditor!.selection.end,
),
content,
);
}
});
}
當前目錄翻譯成英文
代碼:
import * as path from 'path';
import * as vscode from 'vscode';
import * as fs from 'fs-extra';
import { context } from './context';
export async function bootstrap() {
const { lowcodeContext } = context;
const explorerSelectedPath = path
.join(lowcodeContext?.explorerSelectedPath || '')
.replace(/\\/g, '/');
const explorerSelectedPathArr = explorerSelectedPath.split('/');
const name = explorerSelectedPathArr.pop();
vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
},
async (progress) => {
progress.report({
message: `loading`,
});
let content = await context.lowcodeContext!.createChatCompletion({
messages: [
{
role: 'system',
content: `你是一個翻譯家,你的目標是把中文翻譯成英文單詞,請翻譯時使用駝峰格式,小寫字母開頭,不要帶翻譯腔,而是要翻譯得自然、流暢和地道,使用優美和高雅的表達方式。請翻譯下面用戶輸入的內容`,
},
{
role: 'user',
content: name || '',
},
],
});
content = content.charAt(0).toLowerCase() + content.slice(1);
fs.renameSync(
path.join(lowcodeContext?.explorerSelectedPath || ''),
path.join(explorerSelectedPathArr.join('/'), content),
);
},
);
}
快速創建區塊
代碼:
import * as path from 'path';
import { window } from 'vscode';
import * as fs from 'fs-extra';
import { context } from './context';
import { renderEjsTemplates } from '../../../../../share/utils/ejs';
export async function bootstrap() {
const { lowcodeContext } = context;
const result = await window.showQuickPick(
[
'uniapp/vue3-mvp',
'uniapp/vue3-mvp emit',
'uniapp/vue3-mvp props',
'uniapp/vue3-mvp props emit',
].map((s) => s),
{ placeHolder: '請選擇模板' },
);
if (!result) {
return;
}
const tempWorkPath = path.join(
lowcodeContext?.env.rootPath || '',
'.lowcode',
);
fs.copySync(path.join(lowcodeContext?.materialPath || ''), tempWorkPath);
await renderEjsTemplates(
{
createBlockPath: path
.join(lowcodeContext?.explorerSelectedPath || '')
.replace(/\\/g, '/'),
},
path.join(tempWorkPath, 'src'),
);
fs.copySync(
path.join(tempWorkPath, 'src', result),
path.join(lowcodeContext?.explorerSelectedPath || ''),
);
fs.removeSync(tempWorkPath);
}
打開 WebView
右邊 WebView 是一個獨立的工程,部署在 vercel 上,主要為了學一下 UnoCSS,后續可能會把 screenshot-to-code 抄過來
完整代碼:https://github.com/lowcode-scaffold/lowcode-materials/tree/master/materials/snippets/打開webview/script
WebView 項目代碼(Vue):lowcode-webview-vue
WebView 項目代碼(React):lowcode-webview-react-vite
無限可能
上面列舉了我常用的一些功能,以及正在嘗試的東西,可以看出 lowcode 插件的*度已經很高了,后續如果出現了什么好玩的技術可以立即接入玩一下。
遺憾
2023 對圖片相關的 AI 研究的比較少,也想不到有什么使用場景。
2024 研究一下 Design to Code + AI 的落地。
源碼
插件源碼:https://github.com/lowcoding/lowcode-vscode
物料源碼:https://github.com/lowcode-scaffold/lowcode-materials
總結
以上是生活随笔為你收集整理的我的2023年度关键词:ChatGPT、生产力工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SAP FICO 资产负债表开发说明书(
- 下一篇: a'a