mindcraft/src/agent/history.js

115 lines
4.8 KiB
JavaScript
Raw Normal View History

2023-11-12 14:53:23 -08:00
import { writeFileSync, readFileSync, mkdirSync } from 'fs';
2024-01-03 22:16:50 -08:00
import { stringifyTurns } from '../utils/text.js';
2024-01-25 13:25:36 -08:00
import { sendRequest } from '../utils/gpt.js';
import { getCommandDocs } from './commands/index.js';
2023-11-12 14:53:23 -08:00
export class History {
2023-12-21 20:42:01 -07:00
constructor(agent) {
2023-11-12 14:53:23 -08:00
this.name = agent.name;
2023-12-21 20:42:01 -07:00
this.save_path = `./bots/${this.name}/save.json`;
2023-11-15 13:18:38 -08:00
this.turns = [];
2023-11-12 14:53:23 -08:00
// These define an agent's long term memory
2023-11-13 20:33:34 -08:00
this.bio = '';
2023-11-12 14:53:23 -08:00
this.memory = '';
2023-11-15 13:18:38 -08:00
// Variables for controlling the agent's memory and knowledge
2023-11-12 14:53:23 -08:00
this.max_messages = 20;
}
2023-08-15 23:39:02 -07:00
2024-01-15 11:04:50 -06:00
async getHistory(examples=null) { // expects an Examples object
2024-01-13 12:10:15 -06:00
let turns = JSON.parse(JSON.stringify(this.turns));
2024-01-15 11:04:50 -06:00
if (examples) {
let examples_msg = await examples.createExampleMessage(turns);
turns = examples_msg.concat(turns);
2023-11-19 15:34:53 -06:00
}
2024-01-15 11:04:50 -06:00
2024-01-13 12:10:15 -06:00
return turns;
}
2023-08-15 23:39:02 -07:00
2023-11-19 15:34:53 -06:00
getSystemMessage() {
2024-01-13 12:10:15 -06:00
let system_message = `You are a playful Minecraft bot named '${this.name}' that can communicate with players, see, move, mine, build, and interact with the world by using commands. Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in your responses, use commands often, and do not give instructions unless asked.
Don't pretend to act, use commands immediately when requested. Do NOT do this: "Sure, I'll follow you! *follows you*", instead do this: "Sure I'll follow you! !followPlayer('steve')". Have fun :) \n`;
2024-01-03 22:16:50 -08:00
system_message += getCommandDocs();
2023-11-15 13:18:38 -08:00
if (this.bio != '')
system_message += '\n\nBio:\n' + this.bio;
if (this.memory != '')
system_message += '\n\nMemory:\n' + this.memory;
2023-11-19 15:34:53 -06:00
2023-11-13 20:33:34 -08:00
return system_message;
}
2023-11-12 14:53:23 -08:00
2023-11-13 20:33:34 -08:00
async storeMemories(turns) {
2023-11-19 15:34:53 -06:00
console.log("To summarize:", turns)
let memory_prompt = 'Update your "Memory" by summarizing the following conversation. Your "Memory" is for storing information that will help you improve as a Minecraft bot. Include details about your interactions with other players that you may need to remember for later. Also include things that you have learned through player feedback or by executing code. Do not include information found in your Docs or that you got right on the first try. Be extremely brief and clear.';
2023-11-13 20:33:34 -08:00
if (this.memory != '') {
2023-11-19 15:34:53 -06:00
memory_prompt += `This is your previous memory: "${this.memory}"\n Include and summarize any relevant information from this previous memory. Your output will replace your previous memory.`;
2023-11-13 20:33:34 -08:00
}
2023-11-19 15:34:53 -06:00
memory_prompt += '\n';
memory_prompt += ' Your output should use one of the following formats:\n';
2023-11-15 11:38:17 -08:00
memory_prompt += '- When the player... output...\n';
memory_prompt += '- I learned that player [name]...\n';
2023-11-19 15:34:53 -06:00
memory_prompt += 'This is the conversation to summarize:\n';
2024-01-03 22:16:50 -08:00
memory_prompt += stringifyTurns(turns);
2023-11-19 15:34:53 -06:00
memory_prompt += 'Summarize relevant information from your previous memory and this conversation:\n';
let memory_turns = [{'role': 'system', 'content': memory_prompt}]
this.memory = await sendRequest(memory_turns, this.getSystemMessage());
2023-11-15 13:18:38 -08:00
}
2023-11-12 14:53:23 -08:00
async add(name, content) {
let role = 'assistant';
if (name === 'system') {
role = 'system';
}
2023-11-13 20:33:34 -08:00
else if (name !== this.name) {
role = 'user';
content = `${name}: ${content}`;
2023-08-17 00:00:57 -07:00
}
this.turns.push({role, content});
2023-11-12 14:53:23 -08:00
// Summarize older turns into memory
if (this.turns.length >= this.max_messages) {
2023-11-15 18:53:02 -06:00
console.log('summarizing memory')
2023-11-15 11:38:17 -08:00
let to_summarize = [this.turns.shift()];
2023-12-04 23:26:21 -08:00
while (this.turns[0].role != 'user' && this.turns.length > 1)
2023-11-15 11:38:17 -08:00
to_summarize.push(this.turns.shift());
await this.storeMemories(to_summarize);
2023-11-12 14:53:23 -08:00
}
}
2023-12-21 20:42:01 -07:00
save() {
2023-11-12 14:53:23 -08:00
// save history object to json file
2023-12-21 20:42:01 -07:00
mkdirSync(`./bots/${this.name}`, { recursive: true });
2023-11-15 13:18:38 -08:00
let data = {
'name': this.name,
'bio': this.bio,
'memory': this.memory,
'turns': this.turns
};
const json_data = JSON.stringify(data, null, 4);
2023-12-21 20:42:01 -07:00
writeFileSync(this.save_path, json_data, (err) => {
2023-11-12 14:53:23 -08:00
if (err) {
throw err;
}
console.log("JSON data is saved.");
});
}
2023-12-21 20:42:01 -07:00
load(profile) {
const load_path = profile? `./bots/${this.name}/${profile}.json` : this.save_path;
2023-11-12 14:53:23 -08:00
try {
// load history object from json file
2023-12-12 21:35:39 -08:00
const data = readFileSync(load_path, 'utf8');
2023-11-12 14:53:23 -08:00
const obj = JSON.parse(data);
this.bio = obj.bio;
this.memory = obj.memory;
2023-12-04 23:26:21 -08:00
this.turns = obj.turns;
2023-11-12 14:53:23 -08:00
} catch (err) {
2023-12-21 20:42:01 -07:00
console.error(`No file for profile '${load_path}' for agent ${this.name}.`);
2023-11-12 14:53:23 -08:00
}
2023-08-17 00:00:57 -07:00
}
}