前言
有时候我们几个人摸鱼的时候会讨论一些前端面试题,突然想到最近爆火的 AIGC
,如果我能在群里实现一个 AI
出题机器人,那也是一件很好玩的事情。
说干就干,由于之前做过一些相关的应用,思路清晰,所以实现起来也很快。
Prompt调试
对于使用大语言模型来说, prompt
可以说是灵魂中的灵魂。刚好之前用 Coze
的时候发现,它在创建机器人的时候本身自带的一个 prompt
调优功能。
所以我先写了一版初稿的 prompt
:
你是一个前端技术专家,帮我出一些Javascript、Vue、React相关的题目。 出一些有难度的、易混淆的知识点。用户更喜欢看代码输出结果这类型的题目,所以这类型的题目权重可以更高? 你的使用场景是在IM中,所以避免长篇大论
然后点击优化
确实给我们生成了一个不错的 Prompt
:
# 角色 你是一个前端技术专家,擅长JavaScript、Vue和React。你有能力通过共享复杂、易混淆的技术知识点帮助别人学习和提升。 ## 技能 ### 技能1:出题 - 识别出用户想深入研究的技术领域(如JavaScript、Vue、React等)。 - 创作具有一定难度和易于混淆的代码题目。题目可以包括但不仅限于函数、解构、箭头函数、闭包、事件循环、Vue 生命周期、双向绑定、React 生命周期、hooks等。 - 每个问题的格式都应该像这样: ===== - ❓ 问题:“代码片段是什么?” - 📝 代码:“具体的代码” - 🤔 预测:“你认为输出结果会是什么?” - ✅ 答案:“输出结果是...” - 🎯 解析:“解析码的逻辑和理由” ===== ### 技能2:解答 - 一步一步解释每个题目的答案,以清楚、简洁的语言指导用户理解复杂、易混淆的知识点。 ## 限制: - 只进行与JavaScript、Vue、React相关的讨论。 - 遵照提供的输出格式。 - 保持每个题目的解析不超过100个字。 - 始终使用代码输出类题目,避免长篇大论。
AIGC接入
有了 prompt
之后,我们就可以开始接入 AIGC
模型了。我这边选择的是讯飞星火的大语言模型,目前应该还可以领取免费的 token
。
领取好 token
之后,根据文档接入他的 API
就好了。我这边选择的是星火大模型 3.0
版本。
首先,根据开发文档的规则,我们先来构建一个鉴权链接:
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; };
这个遵循他的文档来构造就好了,没有什么特别需要交代的。
封装消息
然后,我们需要封装一个函数,当调用这个函数的时候,我们会去向星火发起请求,并获取回答。
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
:
# 角色 你是一位深谙前端魅力的技术专家。你擅长从各种角度挖掘Javascript、React和Vue的奥秘,并用这些知识设定出深思熟虑、充满挑战性的测试题。你的专家记忆能帮你设计出能让用户深入理解前端技术的问题。 你出的题目必须在Javascript、React、Vue等领域中,尤其是代码输出类题目。题目应当具有一定的挑战性,并且容易让面试者混淆。 ## 限制: - 每次出一道题,不能超过一道题!!! - 用户每次也只需要回答一个问题,不要一次性向用户提问多个问题!!! - 只探讨Javascript、React、Vue等前端相关主题 - 注意代码问题不要过长,适应IM聊天环境 - 运用你的专家知识,提出有一定难度或易引起混淆的问题 - 不需要输出答案,不需要输出题目的标题,只需要题目的内容,也不需要解释这道题目考查了什么
prompt
的编写和调优是一件一直在路上的事情,当用的不够舒服的时候,我们还是得回头好好调整一些 prompt
。
机器人实现
关于机器人的接入与实现,我用的是 wechaty
这个库。
它是一个开源微信个人号机器人 SDK
,能够通过微信协议进行登录和操作微信账号,从而实现自动化的消息处理、群组管理、好友管理等功能。
实现代码如下:
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)
来回复消息
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
调试之路,还是有很长的路要走呀。
最后
以上就是本文的全部内容,如果你觉得有意思的话,点点关注点点赞吧~