This commit is contained in:
手瓜一十雪
2025-02-24 16:49:55 +08:00
parent d6485b220e
commit 5c98623f2b
2 changed files with 83 additions and 56 deletions

View File

@@ -1,5 +1,5 @@
import { OB11EmitEventContent, OB11NetworkReloadType } from './index';
import { NapCatOneBot11Adapter, OB11ArrayMessage, OB11Message } from '@/onebot';
import { NapCatOneBot11Adapter, OB11ArrayMessage } from '@/onebot';
import { NapCatCore } from '@/core';
import { PluginConfig } from '../config/config';
import { plugin_onmessage } from '@/plugin';
@@ -15,7 +15,7 @@ export class OB11PluginAdapter extends IOB11NetworkAdapter<PluginConfig> {
messagePostFormat: 'array',
reportSelfMessage: false,
enable: true,
debug: false,
debug: true,
};
super(name, config, core, obContext, actions);
}

View File

@@ -1,5 +1,5 @@
import { NapCatOneBot11Adapter, OB11ArrayMessage, OB11MessageDataType } from '@/onebot';
import { ChatType, NapCatCore, Peer, RawMessage } from '@/core';
import { NapCatCore, RawMessage } from '@/core';
import { ActionMap } from '@/onebot/action';
import { OB11PluginAdapter } from '@/onebot/network/plugin';
import { OpenAI } from 'openai';
@@ -8,7 +8,10 @@ const API_KEY = 'sk-vDXUiGa1fx8ygDJDjAlr2rUyoz3uPhMxr8zId8n3ycMkV23i';
const BASE_URL = 'https://api.bili2233.work/v1';
const MODEL = 'gemini-2.0-flash-thinking-exp';
const SHORT_TERM_MEMORY_LIMIT = 100;
const BOT_NAME = '千千';
const PROMPT =
`你的名字叫千千,你现在处于一个QQ群聊之中,作为博学多识的可爱群员,热心解答各种问题和高强度水群
记住你说的话要尽量的简洁但具有情感,不要长篇大论,一句话不宜超过五十个字。`;
const client = new OpenAI({
apiKey: API_KEY,
baseURL: BASE_URL
@@ -16,6 +19,7 @@ const client = new OpenAI({
const longTermMemory: Map<string, string> = new Map();
const shortTermMemory: Map<string, string[]> = new Map();
const memoryTransferCount: Map<string, number> = new Map();
async function createChatCompletionWithRetry(params: any, retries: number = 3): Promise<any> {
for (let attempt = 0; attempt < retries; attempt++) {
@@ -28,25 +32,6 @@ async function createChatCompletionWithRetry(params: any, retries: number = 3):
}
}
async function handleMessageArray2String(messages: RawMessage[]): Promise<string[]> {
const result: string[] = [];
let data = '';
for (let i = 0; i < messages.length; i++) {
try {
if (messages[i]) {
data += await handleMessage2String(messages[i]!) + '\n';
}
} catch {
continue;
}
if ((i + 1) % 1000 === 0 || i === messages.length - 1) {
result.push(data);
data = '';
}
}
return result;
}
async function handleMessage2String(message: RawMessage): Promise<string> {
let data = '';
for (let element of message.elements) {
@@ -56,7 +41,7 @@ async function handleMessage2String(message: RawMessage): Promise<string> {
if (element.replyElement) {
const records = message.records.find(msgRecord => msgRecord.msgId === element.replyElement?.sourceMsgIdInRecords);
if (records) {
data += '[Reply] 回应别人的消息 ->' + await handleMessage2String(records) + '<-';
data += '[Reply] 回应内容 源消息[' + await handleMessage2String(records) + ']';
}
}
}
@@ -65,10 +50,7 @@ async function handleMessage2String(message: RawMessage): Promise<string> {
}
function updateMemoryLayer(memoryLayer: Map<string, string[]>, group_id: string, newMessages: string[]) {
if (!memoryLayer.has(group_id)) {
memoryLayer.set(group_id, []);
}
const currentMemory = memoryLayer.get(group_id)!;
const currentMemory = memoryLayer.get(group_id) || [];
currentMemory.push(...newMessages);
if (currentMemory.length > SHORT_TERM_MEMORY_LIMIT) {
memoryLayer.set(group_id, currentMemory.slice(-SHORT_TERM_MEMORY_LIMIT));
@@ -85,6 +67,7 @@ async function mergeAndUpdateMemory(existing_memories: string, new_memory: strin
- 如果一个记忆直接与新信息矛盾,请批判性地评估两条信息:
- 如果新记忆提供了更近期或更准确的更新,用新记忆替换旧记忆。
- 如果新记忆看起来不准确或细节较少,保留旧记忆并丢弃新记忆。
- 注意区分对应人物的记忆和印象, 不要产生混淆人物的印象和记忆。
- 在所有记忆中保持一致且清晰的风格,确保每个条目简洁而信息丰富。
- 如果新记忆是现有记忆的变体或扩展,更新现有记忆以反映新信息。
以下是任务的详细信息:
@@ -102,24 +85,61 @@ ${new_memory}`;
}
async function generateChatCompletion(content_data: string): Promise<string> {
console.log(content_data);
const chatCompletion = await createChatCompletionWithRetry({
messages: [{ role: 'user', content: content_data }],
model: MODEL
});
console.log(chatCompletion);
return chatCompletion.choices[0]?.message.content || '';
}
async function updateMemory(group_id: string, newMessages: string[]) {
updateMemoryLayer(shortTermMemory, group_id, newMessages);
async function updateMemory(group_id: string, newMessages: string) {
updateMemoryLayer(shortTermMemory, group_id, [newMessages]);
const currentMemory = longTermMemory.get(group_id) || '';
const transferCount = memoryTransferCount.get(group_id) || 0;
if (shortTermMemory.get(group_id)!.length >= SHORT_TERM_MEMORY_LIMIT) {
const mergedMemory = await mergeAndUpdateMemory(currentMemory, shortTermMemory.get(group_id)!.join('\n'));
longTermMemory.set(group_id, mergedMemory);
shortTermMemory.set(group_id, []);
memoryTransferCount.set(group_id, transferCount + 1);
if (memoryTransferCount.get(group_id)! >= 1) {
const mergedMemory = await mergeAndUpdateMemory(currentMemory, shortTermMemory.get(group_id)!.join('\n'));
longTermMemory.set(group_id, mergedMemory);
shortTermMemory.set(group_id, []);
memoryTransferCount.set(group_id, 0);
}
}
}
async function clearShortTermMemory(group_id: string) {
shortTermMemory.set(group_id, []);
memoryTransferCount.set(group_id, 0);
}
async function clearLongTermMemory(group_id: string) {
longTermMemory.set(group_id, '');
}
async function handleClearMemoryCommand(group_id: string, type: 'short' | 'long', action: ActionMap, adapter: string, instance: OB11PluginAdapter) {
if (type === 'short') {
await clearShortTermMemory(group_id);
await sendGroupMessage(group_id, '短期上下文已清理', action, adapter, instance);
} else {
await clearLongTermMemory(group_id);
await sendGroupMessage(group_id, '长期上下文已清理', action, adapter, instance);
}
}
async function sendGroupMessage(group_id: string, text: string, action: ActionMap, adapter: string, instance: OB11PluginAdapter) {
await action.get('send_group_msg')?.handle({
group_id: String(group_id),
message: [
{
type: OB11MessageDataType.text,
data: { text }
}
]
}, adapter, instance.config);
}
export const plugin_onmessage = async (
adapter: string,
core: NapCatCore,
@@ -133,35 +153,42 @@ export const plugin_onmessage = async (
if (!user_uid) {
return;
}
const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: message.group_id?.toString() ?? '' };
const msg = await core.apis.MsgApi.queryFirstMsgBySender(peer, [user_uid]);
if (msg.msgList.length < 1) {
let msg_string = '';
try {
msg_string = await handleMessage2String(message.raw!);
} catch (error) {
if (msg_string == '') {
return;
}
}
const user_info = await action.get('get_group_member_info')?.handle({ group_id: message.group_id?.toString()!, user_id: user_id }, adapter, instance.config);
//const text = `${message.sender.nickname}(${message.sender.user_id})发送了消息 : ${msg_string}`;
if (message.raw_message === '/清除短期上下文') {
await handleClearMemoryCommand(message.group_id?.toString()!, 'short', action, adapter, instance);
return;
}
const msg_string_all = await handleMessageArray2String(msg.msgList);
const user_info = await action.get('get_group_member_info')?.handle({ group_id: message.group_id?.toString()!, user_id: user_id }, adapter, instance.config);
const msg_string = msg_string_all.join('\n');
await updateMemory(message.group_id?.toString()!, msg_string_all);
if (message.raw_message === '/清除长期上下文') {
await handleClearMemoryCommand(message.group_id?.toString()!, 'long', action, adapter, instance);
return;
}
const orimsgid = message.message.find(e => e.type == 'reply')?.data.id;
const orimsg = orimsgid ? await action.get('get_msg')?._handle({ message_id: orimsgid }, adapter, instance.config) : undefined;
if (
!message.raw_message.startsWith(BOT_NAME) &&
!message.message.find(e => e.type == 'at' && e.data.qq == core.selfInfo.uin) &&
orimsg?.sender.user_id.toString() !== core.selfInfo.uin
) {
return;
}
await updateMemory(message.group_id?.toString()!, msg_string);
const longTermMemoryString = longTermMemory.get(message.group_id?.toString()!) || '';
const shortTermMemoryString = shortTermMemory.get(message.group_id?.toString()!)?.join('\n') || '';
const content_data =
`请根据下面聊天内容,继续与 ${user_info?.data?.card || user_info?.data?.nickname} 进行对话。注意回复内容只用输出内容,不要提及此段话,注意一定不要使用markdown,请采用纯文本回复。长时间记忆:\n${longTermMemoryString}\n短时间记忆:\n${shortTermMemoryString}\n当前对话:\n${msg_string}\n}`;
console.log(`Final content data: ${content_data}`);
`请根据下面聊天内容,继续与 ${user_info?.data?.card || user_info?.data?.nickname} 进行对话。注意回复内容只用输出内容,不要提及此段话,注意一定不要使用markdown,请采用纯文本回复。你的人设:${PROMPT}长时间记忆:\n${longTermMemoryString}\n短时间记忆:\n${shortTermMemoryString}\n当前对话:\n${msg_string}\n}`;
const msg_ret = await generateChatCompletion(content_data);
console.log(`Final content ret: ${msg_ret}`);
await action.get('send_group_msg')?.handle({
group_id: String(message.group_id),
message: [
{
type: OB11MessageDataType.text,
data: { text: msg_ret }
}
]
}, adapter, instance.config);
await sendGroupMessage(message.group_id?.toString()!, msg_ret, action, adapter, instance);
};