100行代码,我给摸鱼群加了一个前端出题机器人

前言

有时候我们几个人摸鱼的时候会讨论一些前端面试题,突然想到最近爆火的 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);
  });
};

解释一下上面的代码:

  1. 使用第三方包 websocket 去构造一个客户端,用来发起 ws 请求
  2. 使用上述的鉴权链接作为 wsurl
  3. 把你需要发的 prompt 以及一下其他参数组装一下发给星火模型
  4. 开始接收到请求之后,由于星火模型的响应是分段的
    • data.payload.choices.text[0].content是当前响应的这一段,把每一段拼起来
    • data.payload.choices.status表示响应的状态, status2 时,表示响应结束
  5. 响应结束后关闭 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 调试之路,还是有很长的路要走呀。

最后

以上就是本文的全部内容,如果你觉得有意思的话,点点关注点点赞吧~

相关推荐
博客zhu虎康2 分钟前
ElementUI 的 form 表单校验
前端·javascript·elementui
敲啊敲952731 分钟前
5.npm包
前端·npm·node.js
CodeClimb40 分钟前
【华为OD-E卷-木板 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
咸鱼翻面儿44 分钟前
Javascript异步,这次我真弄懂了!!!
javascript
qq_589568101 小时前
Echarts的高级使用,动画,交互api
前端·javascript·echarts
j喬乔1 小时前
Node导入不了命名函数?记一次Bug的探索
typescript·node.js
暴富的Tdy3 小时前
【CryptoJS库AES加密】
前端·javascript·vue.js
℘团子এ3 小时前
js和html中,将Excel文件渲染在页面上
javascript·html·excel
云边有个稻草人3 小时前
AIGC与娱乐产业:颠覆创意与生产的新力量
aigc·娱乐