前言
有时候我们几个人摸鱼的时候会讨论一些前端面试题,突然想到最近爆火的 AIGC
,如果我能在群里实现一个 AI
出题机器人,那也是一件很好玩的事情。
说干就干,由于之前做过一些相关的应用,思路清晰,所以实现起来也很快。
Prompt调试
对于使用大语言模型来说, prompt
可以说是灵魂中的灵魂。刚好之前用 Coze
的时候发现,它在创建机器人的时候本身自带的一个 prompt
调优功能。
所以我先写了一版初稿的 prompt
:
你是一个前端技术专家,帮我出一些Javascript、Vue、React相关的题目。
出一些有难度的、易混淆的知识点。用户更喜欢看代码输出结果这类型的题目,所以这类型的题目权重可以更高?
你的使用场景是在IM中,所以避免长篇大论
然后点击优化
确实给我们生成了一个不错的 Prompt
:
markdown
# 角色
你是一个前端技术专家,擅长JavaScript、Vue和React。你有能力通过共享复杂、易混淆的技术知识点帮助别人学习和提升。
## 技能
### 技能1:出题
- 识别出用户想深入研究的技术领域(如JavaScript、Vue、React等)。
- 创作具有一定难度和易于混淆的代码题目。题目可以包括但不仅限于函数、解构、箭头函数、闭包、事件循环、Vue 生命周期、双向绑定、React 生命周期、hooks等。
- 每个问题的格式都应该像这样:
=====
- ❓ 问题:"代码片段是什么?"
- 📝 代码:"具体的代码"
- 🤔 预测:"你认为输出结果会是什么?"
- ✅ 答案:"输出结果是..."
- 🎯 解析:"解析码的逻辑和理由"
=====
### 技能2:解答
- 一步一步解释每个题目的答案,以清楚、简洁的语言指导用户理解复杂、易混淆的知识点。
## 限制:
- 只进行与JavaScript、Vue、React相关的讨论。
- 遵照提供的输出格式。
- 保持每个题目的解析不超过100个字。
- 始终使用代码输出类题目,避免长篇大论。
AIGC接入
有了 prompt
之后,我们就可以开始接入 AIGC
模型了。我这边选择的是讯飞星火的大语言模型,目前应该还可以领取免费的 token
。
领取好 token
之后,根据文档接入他的 API
就好了。我这边选择的是星火大模型 3.0
版本。
首先,根据开发文档的规则,我们先来构建一个鉴权链接:
js
import CryptoJS from "crypto-js";
const getWebsocketUrl = () => {
const apiKey = APIKey;
const apiSecret = APISecret;
let url = "wss://spark-api.xf-yun.com/v3.1/chat";
const host = "localhost:5173";
const date = new Date().toGMTString();
const algorithm = "hmac-sha256";
const headers = "host date request-line";
const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v3.1/chat HTTP/1.1`;
const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
const signature = CryptoJS.enc.Base64.stringify(signatureSha);
const authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
const authorization = btoa(authorizationOrigin);
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
return url;
};
这个遵循他的文档来构造就好了,没有什么特别需要交代的。
封装消息
然后,我们需要封装一个函数,当调用这个函数的时候,我们会去向星火发起请求,并获取回答。
js
import websocket from "websocket";
const getContent = async () => {
return new Promise((resolve, reject) => {
const url = getWebsocketUrl();
const WebSocket = websocket.client;
const wsClient = new WebSocket();
const wsUrl = url;
wsClient.on("connect", (connection) => {
console.log("WebSocket 连接成功");
const prompt = '你的prompt';
const params = {
header: {
app_id: APPID,
uid: "jayliang",
},
parameter: {
chat: {
domain: "generalv3",
temperature: 0.5,
max_tokens: 1024,
},
},
payload: {
message: {
text: [{ role: "user", content: prompt }],
},
},
};
let res = "";
connection.on("message", (message) => {
if (message.type === "utf8") {
const data = JSON.parse(message.utf8Data);
const status = data.payload.choices.status;
const content = data.payload.choices.text[0].content;
if (content) {
res += content;
}
if (status === 2) {
resolve(res);
connection.close();
}
}
});
connection.sendUTF(JSON.stringify(params));
});
wsClient.on("close", () => {
console.log("WebSocket 连接关闭");
});
wsClient.on("error", (error) => {
console.error("WebSocket 连接错误:", error);
reject(error);
});
wsClient.connect(wsUrl);
});
};
解释一下上面的代码:
- 使用第三方包
websocket
去构造一个客户端,用来发起ws
请求 - 使用上述的鉴权链接作为
ws
的url
- 把你需要发的
prompt
以及一下其他参数组装一下发给星火模型 - 开始接收到请求之后,由于星火模型的响应是分段的
data.payload.choices.text[0].content
是当前响应的这一段,把每一段拼起来data.payload.choices.status
表示响应的状态,status
为2
时,表示响应结束
- 响应结束后关闭
socket
链接,并返回组装好的结果
prompt小插曲
但是当我使用上面 Coze
帮我调优的 prompt
发给星火的时候,他给我的答案并不是我想要的,这一点就很玄学。所以我只能自己动手写一份 prompt
:
markdown
# 角色
你是一位深谙前端魅力的技术专家。你擅长从各种角度挖掘Javascript、React和Vue的奥秘,并用这些知识设定出深思熟虑、充满挑战性的测试题。你的专家记忆能帮你设计出能让用户深入理解前端技术的问题。
你出的题目必须在Javascript、React、Vue等领域中,尤其是代码输出类题目。题目应当具有一定的挑战性,并且容易让面试者混淆。
## 限制:
- 每次出一道题,不能超过一道题!!!
- 用户每次也只需要回答一个问题,不要一次性向用户提问多个问题!!!
- 只探讨Javascript、React、Vue等前端相关主题
- 注意代码问题不要过长,适应IM聊天环境
- 运用你的专家知识,提出有一定难度或易引起混淆的问题
- 不需要输出答案,不需要输出题目的标题,只需要题目的内容,也不需要解释这道题目考查了什么
prompt
的编写和调优是一件一直在路上的事情,当用的不够舒服的时候,我们还是得回头好好调整一些 prompt
。
机器人实现
关于机器人的接入与实现,我用的是 wechaty
这个库。
它是一个开源微信个人号机器人 SDK
,能够通过微信协议进行登录和操作微信账号,从而实现自动化的消息处理、群组管理、好友管理等功能。
实现代码如下:
js
import { WechatyBuilder } from "wechaty";
const wechaty = WechatyBuilder.build();
wechaty
.on("scan", (qrcode, status) =>
console.log(
`Scan QR Code to login: ${status}\nhttps://wechaty.js.org/qrcode/${encodeURIComponent(
qrcode
)}`
)
)
.on("login", (user) => console.log(`User ${user} logged in`))
.on("message", async (message) => {
console.log('message', message)
})
.on("logout", (...args) => {
console.log(...args);
});
wechaty.start();
当启动这个程序的时候,控制台会打印一个 url
,点开 url
使用微信扫码,就可以在程序中登录你的微信了。
然后,我们判断一下,在群聊中收到消息时,就调用 getContent
去生成一道题目,并发送到当前群聊中:
- 调用
message.room()
方法检查消息是否来自群聊 - 调用
message.mentionSelf()
方法来判断消息中是否@了机器人 message.type() === wechaty.Message.Type.Text
是否为文本消息- 调用大模型获取结果,然后调用
message.say(content)
来回复消息
js
try {
if (message.room()) {
const mentionSelf = await message.mentionSelf();
if (message.type() === wechaty.Message.Type.Text && mentionSelf) {
const content = await getContent();
await message.say(content);
}
}
} catch (error) {
message.say("发生了一些错误😷");
}
整体功能是没什么问题了,只是这个题目的难度确实有点低,不太满足我们一开始给他的需求。所以 prompt
调试之路,还是有很长的路要走呀。
最后
以上就是本文的全部内容,如果你觉得有意思的话,点点关注点点赞吧~