Skip to content

Commit

Permalink
chore: improve coze bot
Browse files Browse the repository at this point in the history
  • Loading branch information
adaex committed May 22, 2024
1 parent ea713ea commit a717213
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 66 deletions.
63 changes: 50 additions & 13 deletions scripts/llm/code.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import _ from 'lodash';
import { statSync, readdirSync, readFileSync } from 'fs';
import { join } from 'path';

///// 辅助函数 /////
// 递归函数来遍历文件夹
function walkDir(dir, callback) {
readdirSync(dir).forEach(f => {
Expand All @@ -10,27 +12,44 @@ function walkDir(dir, callback) {
});
}

export class SourceCode {
// 获取全部文件列表
getFileList(dir, reg) {
const list = [];
walkDir(dir, function (filePath) {
if (filePath.match(reg)) {
list.push(filePath);
// 源代码相关处理类,获取文件列表
class Code {
constructor(dir, fileReg) {
this._dir = dir;
this._fileReg = fileReg;
}
// 获取文件列表
fileList() {
const fileList = [];
walkDir(this._dir, filePath => {
if (filePath.match(this._fileReg)) {
fileList.push(filePath);
}
});
return list;
return fileList;
}

getDirectoryContent(dir, reg) {}
// 获取目录内容
directory() {
const fileList = this.fileList();

// 生成目录结构
const contents = [
`The source code directory is ${this._dir}, which contains the following files:`,
...fileList.map(i => '- ' + i),
'\n',
];

// 获取某个目录下所有的源代码
getCodeContent(dir, reg) {
const fileList = this.getFileList(dir, reg);
return contents.join(',');
}

// 获取目录内容和文件内容
directoryAndContent() {
const fileList = this.fileList();

// 生成目录结构
const contents = [
`The source code directory is ${dir}, which contains the following files:`,
`The source code directory is ${this.fileList}, which contains the following files:`,
...fileList.map(i => '- ' + i),
'\n',
];
Expand All @@ -43,3 +62,21 @@ export class SourceCode {
return contents.join('\n');
}
}

// React 源代码
export class ReactCode extends Code {
constructor(comp) {
const dir = `packages/arcodesign/components/${_.snakeCase(comp)}/`;
const fileReg = /.(ts|tsx|js|jsx|less)$/;
super(dir, fileReg);
}
}

// Vue 源代码
export class VueCode extends Code {
constructor(comp) {
const dir = `packages/arcodesign-vue/components/${_.snakeCase(comp)}/`;
const fileReg = /.(vue|ts|js|less)$/;
super(dir, fileReg);
}
}
124 changes: 111 additions & 13 deletions scripts/llm/coze.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import axios from 'axios';
import _ from 'lodash';
import { writeFileSync, appendFileSync } from 'fs';
import { message } from 'gulp-typescript/release/utils';

///// 环境变量 /////
///// 一些常量 /////
const CHAT_URL = 'https://bots.byteintl.net/open_api/v2/chat';

// 记录日志
export class ChatLog {
// 对话日志,基础类
class ChatLog {
constructor() {
// 初始化变量
this.chatHistory = [];
Expand All @@ -27,27 +26,85 @@ export class ChatLog {
_append(content) {
this.logFiles.forEach(i => appendFileSync(i, content));
}
}

// 写入查询日志
// 对话日志(非流式)
class ChatLogNonStream extends ChatLog {
// 处理查询日志
query(query) {
this._append(`${new Date().toISOString()} Query:\n${query} \n\n`);
this.chatHistory.push({ role: 'user', content_type: 'text', content: query });
}

// 写入响应日志
// 处理响应日志(非流式),并返回 answer
response(data) {
this._append(`${new Date().toISOString()} `);
let answer = '';
data?.messages?.forEach(({ type, content }) => {
if (type === 'verbose') return;
if (type === 'answer') answer += content;
this._append(`${_.startCase(type)}:\n${content}\n\n`);
this.chatHistory.push({ role: 'assistant', content_type: 'text', type, content });
});
return answer;
}
}

export class AiBot {
// 对话日志(流式)
class ChatLogStream extends ChatLog {
// 处理查询日志
query(query) {
this._append(`${new Date().toISOString()} Query:\n${query} \n\n`);
this.chatHistory.push({ role: 'user', content_type: 'text', content: query });
}

// 开始写入响应日志(分块)
responseChunkStart() {
this._chunk = {};
this._append(`${new Date().toISOString()} Response:\n`);
}
// 写入响应日志(分块)
responseChunk(raw) {
const regex = /data:(\{.*?\})(?=\s|$)/g;
const matches = String(raw).match(regex);

for (const match of matches) {
const data = JSON.parse(match.replace('data:', ''));

const event = data?.event,
type = data?.message?.type,
content = data?.message?.content;

// 拼接数据
if (event === 'message' && content) {
if (!this._chunk?.[type]) this._chunk[type] = '';
this._chunk[type] += content;

// 处理 answer
if (type === 'answer') {
this._append(content);
}
}
}
}
// 结束响应(分块)
responseChunkEnd() {
this._append(`\n\n`);
_.forEach(this._chunk, (content, type) => {
if (type === 'verbose') return;
if (type !== 'answer') {
this._append(`${_.startCase(type)}:\n${content}\n\n`);
}
this.chatHistory.push({ role: 'assistant', content_type: 'text', type, content });
});
return this._chunk['answer'];
}
}

// AI 机器人
export class AiBotNonStream {
constructor(botId, token) {
this.log = new ChatLog();
this.log = new ChatLogNonStream();
this.botId = botId;
this.token = token;
}
Expand All @@ -69,12 +126,53 @@ export class AiBot {

// 开始请求
const res = await axios.post(CHAT_URL, body, { headers });
this.log.response(res.data);
const answer = this.log.response(res.data);

return answer;
}
}

export class AiBot {
constructor(botId, token) {
this.log = new ChatLogStream();
this.botId = botId;
this.token = token;
}

// 流式对话
async chat(query, withHistory = true) {
query = query.trim();

const headers = { Authorization: `Bearer ${this.token}` };
const body = {
conversation_id: this.log.chatId,
bot_id: this.botId,
user: 'Aex',
query,
chat_history: withHistory ? this.log.chatHistory : [],
stream: true, // 将请求变为流式
};
this.log.query(query);

// 开始请求 - 注意这里使用流式处理方式
const res = await axios.post(CHAT_URL, body, { headers, responseType: 'stream' });

this.log.responseChunkStart();

// 设置一个数组来收集流式数据片段
res.data.on('data', chunk => {
this.log.responseChunk(chunk.toString('utf-8'));
});

return new Promise((resolve, reject) => {
res.data.on('end', () => {
const data = this.log.responseChunkEnd();
resolve(data);
});

// 拼装结果
const messages = data?.messages?.map(({ type, content }) => {
return type === 'answer' ? content : '';
res.data.on('error', err => {
reject(err);
});
});
return messages.join('');
}
}
18 changes: 18 additions & 0 deletions scripts/llm/gen-article.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { cwd } from 'process';
import { AiBot } from './coze.mjs';

const BOT_ID = '7369473402367361041'; // GPT-4 模型
const TOKEN = process.env.COZE_TOKEN_ADM;

const prompt_article = `
今天又是努力工作的一天,帮我写一篇今天的日记,10 字左右
`;

///// 主流程开始 /////

const bot = new AiBot(BOT_ID, TOKEN);
// 主指令
const answer = await bot.chat(prompt_article);

console.log(answer);
28 changes: 28 additions & 0 deletions scripts/llm/gen-game.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { AiBot } from './coze.mjs';

const BOT_ID = '7369473402367361041'; // GPT-4 模型
const TOKEN = process.env.COZE_TOKEN_ADM;

// 一个游戏,测试 AI 上下文能力
const prompt_game = `
现在我们玩一个游戏:
当我发送一个数字,你应该将这个数字+1,并将结果发给我。
当我发送 "Next",你应该继续+1,并将结果发给我。
当再次接收到数字后,游戏重新开始。
严格按照游戏规则回复,不要回复其他内容。
首先我给你一个数字是 1
`;

///// 主流程开始 /////

const bot = new AiBot(BOT_ID, TOKEN);
// 主指令
await bot.chat(prompt_game);
await bot.chat('Next');
await bot.chat('Next');
await bot.chat('Next');
await bot.chat('Next');
Loading

0 comments on commit a717213

Please sign in to comment.