This commit is contained in:
Kolby Nottingham 2024-12-09 16:30:44 -08:00
parent a7e529b1e9
commit 4e0611d29e
16 changed files with 265 additions and 577 deletions

3
.gitignore vendored
View file

@ -13,6 +13,3 @@ services/viaproxy/plugins/**
services/viaproxy/ViaLoader/**
services/viaproxy/saves.json
services/viaproxy/viaproxy.yml
profiles/task_*
multi_agent_task*
*_results.txt

109
example_tasks.json Normal file
View file

@ -0,0 +1,109 @@
{
"debug_single_agent": {
"goal": "Just stand at a place and don't do anything",
"guidance": null,
"initial_inventory": {},
"type": "debug"
},
"debug_multi_agent": {
"goal": "Just stand at a place and don't do anything",
"agent_names": [
"andy",
"randy"
],
"agent_number": 2,
"initial_inventory": {
"andy": {
"iron_ingot": 1
},
"randy": {
"iron_ingot": 1
}
},
"type": "debug"
},
"construction": {
"type": "construction",
"goal": "Build a house"
},
"multiagent_techtree_1_shears_with_2_iron_ingot": {
"goal": "Collaborate with other agents to build a shear.",
"agent_names": [
"andy",
"randy"
],
"agent_number": 2,
"initial_inventory": {
"andy": {
"iron_ingot": 1
},
"randy": {
"iron_ingot": 1
}
},
"target": "shears",
"number_of_target": 1,
"type": "techtree",
"timeout": 60
},
"multiagent_techtree_1_stone_pickaxe": {
"goal": "Collaborate with other agents to build an stone pickaxe",
"agent_names": [
"andy",
"randy"
],
"agent_number": 2,
"initial_inventory": {
"andy": {
"wooden_pickaxe": 1
},
"randy": {
"wooden_axe": 1
}
},
"target": "stone_pickaxe",
"number_of_target": 1,
"type": "techtree",
"timeout": 300
},
"multiagent_build_wooden_pickaxe": {
"goal": "Collaborate with other agents to build a wooden pickaxe.",
"agent_names": [
"andy",
"randy"
],
"agent_number": 2,
"initial_inventory": {
"andy": {
"oak_log": 2
},
"randy": {
"stick": 2
}
},
"target": "wooden_pickaxe",
"number_of_target": 1,
"type": "techtree",
"timeout": 120
},
"multiagent_techtree_boat": {
"goal": "Collaborate with other agents to build a birch boat.",
"agent_names": [
"Bob",
"Alice"
],
"agent_number": 2,
"initial_inventory": {
"Bob": {
"birch_planks": 3
},
"Alice": {
"birch_planks": 2
}
},
"target": "birch_boat",
"number_of_target": 1,
"type": "techtree",
"timeout": 60
}
}

107
main.js
View file

@ -1,9 +1,7 @@
import { AgentProcess } from './src/process/agent-process.js';
import settings from './settings.js';
import yargs from 'yargs';
import { loadTask } from './src/utils/tasks.js';
import { hideBin } from 'yargs/helpers';
import { readFileSync, writeFileSync } from 'fs';
import { createMindServer } from './src/server/mind_server.js';
function parseArguments() {
@ -12,124 +10,39 @@ function parseArguments() {
type: 'array',
describe: 'List of agent profile paths',
})
.option('task', {
.option('task_path', {
type: 'string',
describe: 'Path to task file to execute'
})
.option('task_id', {
type: 'string',
describe: 'Task ID to execute'
})
.option('model', {
type: 'string',
describe: 'LLM model to use',
})
.help()
.alias('help', 'h')
.parse();
}
function updateProfile(profile, args) {
var temp_profile = JSON.parse(readFileSync(profile, 'utf8'));
temp_profile.model = args.model;
writeFileSync(profile, JSON.stringify(temp_profile, null, 2));
return profile;
}
//todo: modify for multiple agents
function getProfiles(args) {
if (args.task) {
var task = loadTask(args.task);
}
if (args.model) {
if (! args.task) {
settings.profiles = settings.profiles.map(x => updateProfile(x, args));
}
else {
if ('agent_number' in task && task.agent_number > 1) {
updateProfile('./multiagent_prompt_desc.json', args);
}
else {
updateProfile('./task_andy.json', args);
}
}
}
if (args.task) {
var task = loadTask(args.task);
if ('agent_number' in task && task.agent_number > 1) {
var profile = JSON.parse(readFileSync('./multiagent_prompt_desc.json', 'utf8'));
var agent_names = task.agent_names;
var filenames = [];
for (let i=0; i<task.agent_number; i++) {
let temp_profile = profile;
temp_profile.name = agent_names[i];
var filename = `multi_agent_task_${agent_names[i]}.json`;
writeFileSync(filename, JSON.stringify(temp_profile, null, 2));
filenames.push(filename);
}
return filenames;
} else {
return ['./task_andy.json'];
}
}
return args.profiles || settings.profiles;
}
function determine_init_message(task, agent_index) {
if (task) {
if ('agent_number' in task && task.agent_number > 1) {
if (agent_index == 0) {
// first agent gets this init message
return "Immediately start a conversation and collaborate together to complete the task. Share resources and skill sets. Use the !startConversation function if needed."
} // all other agents get this init message
return "Collaborate together to complete the task. Share resources and skill sets."
}
return "Announce your task to everyone and get started with it immediately, set a goal if needed, if cheats are enabled then feel free to use newAction commands, no need to collect or mine or gather any items"
}
return settings.init_message;
}
async function main() {
if (settings.host_mindserver) {
const mindServer = createMindServer();
}
const args = parseArguments();
if (args.task) {
var task = loadTask(args.task);
// Inject task information into process.env for the agent to access
process.env.MINECRAFT_TASK_GOAL = task.goal;
if ('agent_number' in task && task.agent_number > 1) {
process.env.ALL_AGENT_NAMES = task.agent_names;
console.log(`All agents for this task are ${process.env.ALL_AGENT_NAMES}`);
}
}
// todo: do inventory
const profiles = getProfiles(args);
console.log(profiles);
// var { load_memory, init_message } = settings;
var load_memory = settings.load_memory;
var init_message = settings.init_message;
var { load_memory, init_message } = settings;
for (let i=0; i<profiles.length; i++) {
try {
const agent = new AgentProcess();
if (args.task) {
init_message = determine_init_message(task, i);
}
agent.start(profiles[i], load_memory, init_message, i, args.task);
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (err) {
console.error(`Failed to start agent ${profiles[i]}:`, err);
}
const agent = new AgentProcess();
agent.start(profiles[i], load_memory, init_message, i, args.task_path, args.task_id);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
try {

View file

@ -1,19 +0,0 @@
{
"name": "bot_name",
"model": "gpt-4o",
"cooldown": 3000,
"conversing": "You are a task-focused Minecraft bot named $NAME. You have to collaborate with other agents in the world, namely: $OTHER_AGENTS to complete the current task : $TASK_GOAL\nFeel free to ask other agents questions and make a plan to achieve the goal. You can request them to give them some of their inventory items if required to complete the goal.\nYou can see, move, mine, build, and interact with the world by using commands. Act focused on completing your assigned task while being human-like. Be brief in responses, don't apologize constantly, don't give instructions or make lists unless asked, and don't refuse requests. Don't pretend to act, use commands immediately when requested. Do NOT say this: 'Sure, I've stopped.', instead say this: 'Sure, I'll stop. !stop'. Do NOT say this: 'On my way! Give me a moment.', instead say this: 'On my way! !goToPlayer('playername', 3)'. Focus on completing the assigned task efficiently.\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$COMMAND_DOCS\n$EXAMPLES\nConversation Begin:",
"coding": "You are an intelligent mineflayer bot $NAME.You have to collaborate with other agents in the world, namely: $OTHER_AGENTS to complete the current task : $TASK_GOAL\n\nWrite javascript codeblocks to control the mineflayer bot to complete this task. Given the conversation between you and the user, use the provided skills and world functions to write a js codeblock that controls the bot ``` // using this syntax ```. The code will be executed and you will receive its output. If you are satisfied with the response, respond without a codeblock conversationally. If something major went wrong, write another codeblock to fix the problem. Be maximally efficient and task-focused. Do not use commands !likeThis, only use codeblocks. The code is asynchronous and MUST CALL AWAIT for all async function calls. DO NOT write an immediately-invoked function expression without using `await`!! DO NOT WRITE LIKE THIS: ```(async () => {console.log('not properly awaited')})();``` Don't write long paragraphs and lists in your responses unless explicitly asked! Only summarize the code you write with a sentence or two when done. This is extremely important to me, think step-by-step, take a deep breath and good luck!\n$SELF_PROMPT\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$CODE_DOCS\n$EXAMPLES\nConversation:",
"saving_memory": "You are a minecraft bot named $NAME that has been talking and playing minecraft by using commands. Update your memory by summarizing the following conversation and your old memory in your next response. Prioritize preserving important facts, things you've learned, useful tips, and long term reminders. Do Not record stats, inventory, or docs! Only save transient information from your chat history. You're limited to 500 characters, so be extremely brief and minimize words. Compress useful information. \nOld Memory: '$MEMORY'\nRecent conversation: \n$TO_SUMMARIZE\nSummarize your old memory and recent conversation into a new memory, and respond only with the unwrapped memory text: ",
"modes": {
"self_preservation": true,
"unstuck": true,
"cowardice": true,
"self_defense": true,
"hunting": false,
"item_collecting": true,
"torch_placing": true,
"idle_staring": true,
"cheat": false
}
}

View file

@ -6,7 +6,6 @@
"@huggingface/inference": "^2.8.1",
"google-translate-api-x": "^10.7.1",
"groq-sdk": "^0.5.0",
"js-yaml": "^4.1.0",
"minecraft-data": "^3.78.0",
"mineflayer": "^4.23.0",
"mineflayer-armor-manager": "^2.0.1",

View file

@ -13,12 +13,10 @@ import { handleTranslation, handleEnglishTranslation } from '../utils/translator
import { addViewer } from './viewer.js';
import settings from '../../settings.js';
import { serverProxy } from './server_proxy.js';
import { loadTask, TechTreeHarvestValidator } from '../utils/tasks.js';
import {getPosition} from './library/world.js'
import { loadTask, initBotTask, TechTreeHarvestValidator } from '../utils/tasks.js';
export class Agent {
async start(profile_fp, load_mem=false, init_message=null, count_id=0, task=null) {
async start(profile_fp, load_mem=false, init_message=null, count_id=0, task_path=null, task_id=null) {
this.last_sender = null;
try {
if (!profile_fp) {
@ -62,32 +60,17 @@ export class Agent {
save_data = this.history.load();
}
if (task) {
this.task = loadTask(task);
// Load task if provided
if (task_path) {
this.task = loadTask(task_path, task_id);
this.taskTimeout = this.task.timeout || 300;
this.taskStartTime = Date.now();
if (this.task.type === 'harvest' || this.task.type === 'techtree') {
this.validator = new TechTreeHarvestValidator(this.task, this.bot);
}
this.validator = new TechTreeHarvestValidator(this.task, this.bot);
} else {
this.task = null;
this.taskTimeout = null;
this.validator = null;
}
// handle blocked actions
if (this.task && "blocked_actions" in this.task) {
if ("agent_number" in this.task && this.task.agent_number > 1) {
this.blocked_actions = this.task.blocked_actions[this.name];
console.log(`Blocked actions for ${this.name}:`, this.blocked_actions);
} else {
this.blocked_actions = this.task.blocked_actions;
console.log(`Blocked actions:`, this.blocked_actions);
}
}
console.log("Is validated:", this.validator && this.validator.validate());
this.bot.on('login', () => {
@ -103,7 +86,6 @@ export class Agent {
const spawnTimeout = setTimeout(() => {
process.exit(0);
}, 30000);
this.bot.once('spawn', async () => {
try {
clearTimeout(spawnTimeout);
@ -116,103 +98,16 @@ export class Agent {
this.clearBotLogs();
if (this.task) {
this.bot.chat(`/clear ${this.name}`);
console.log(`Cleared ${this.name}'s inventory.`);
}
//wait for a bit so inventory is cleared
await new Promise((resolve) => setTimeout(resolve, 500));
console.log(this.task && "agent_number" in this.task && this.task.agent_number > 1);
if (this.task && "agent_number" in this.task && this.task.agent_number > 1) {
var initial_inventory = this.task.initial_inventory[this.name];
console.log("Initial inventory:", initial_inventory);
} else if (task) {
console.log("Initial inventory:", this.task.initial_inventory);
var initial_inventory = this.task.initial_inventory;
}
if (this.task && "initial_inventory" in this.task) {
console.log("Setting inventory...");
console.log("Inventory to set:", initial_inventory);
for (let key of Object.keys(initial_inventory)) {
console.log('Giving item:', key);
this.bot.chat(`/give ${this.name} ${key} ${initial_inventory[key]}`);
};
//wait for a bit so inventory is set
await new Promise((resolve) => setTimeout(resolve, 500));
console.log("Done giving inventory items.");
}
// Function to generate random numbers
function getRandomOffset(range) {
return Math.floor(Math.random() * (range * 2 + 1)) - range;
}
let human_player_name = null;
// Finding if there is a human player on the server
for (const playerName in this.bot.players) {
const player = this.bot.players[playerName];
if (!isOtherAgent(player.username)) {
console.log('Found human player:', player.username);
human_player_name = player.username
break;
await initBotTask(this.bot, this.task);
await new Promise((resolve) => setTimeout(resolve, 10000));
if (this.task.agent_names && this.task.agent_names.filter(name => !this.bot.players[name]).length) {
console.log(`Missing players/bots: ${missingPlayers.join(', ')}`);
this.cleanKill('Not all required players/bots are present in the world. Exiting.', 4);
}
}
// If there are multiple human players, teleport to the first one
// teleport near a human player if found by default
if (this.task && "agent_number" in this.task) {
var agent_names = this.task.agent_names;
if (human_player_name) {
console.log(`Teleporting ${this.name} to human ${human_player_name}`)
this.bot.chat(`/tp ${this.name} ${human_player_name}`) // teleport on top of the human player
}
else {
this.bot.chat(`/tp ${this.name} ${agent_names[0]}`) // teleport on top of the first agent
}
await new Promise((resolve) => setTimeout(resolve, 200));
}
else if (this.task) {
if (human_player_name) {
console.log(`Teleporting ${this.name} to human ${human_player_name}`)
this.bot.chat(`/tp ${this.name} ${human_player_name}`) // teleport on top of the human player
}
await new Promise((resolve) => setTimeout(resolve, 200));
}
// now all bots are teleport on top of each other (which kinda looks ugly)
// Thus, we need to teleport them to random distances to make it look better
/*
Note : We don't want randomness for construction task as the reference point matters a lot.
Another reason for no randomness for construction task is because, often times the user would fly in the air,
then set a random block to dirt and teleport the bot to stand on that block for starting the construction,
This was done by MaxRobinson in one of the youtube videos.
*/
if (this.task && this.task.type !== 'construction') {
const pos = getPosition(this.bot);
const xOffset = getRandomOffset(5);
const zOffset = getRandomOffset(5);
this.bot.chat(`/tp ${this.name} ${Math.floor(pos.x + xOffset)} ${pos.y + 3} ${Math.floor(pos.z + zOffset)}`);
await new Promise((resolve) => setTimeout(resolve, 200));
}
this._setupEventHandlers(save_data, init_message);
this.startEvents();
await new Promise((resolve) => setTimeout(resolve, 10000));
this.checkAllPlayersPresent();
} catch (error) {
console.error('Error in spawn event:', error);
process.exit(0);
@ -291,18 +186,6 @@ export class Agent {
}
}
checkAllPlayersPresent() {
if (!this.task || !this.task.agent_names) {
return;
}
const missingPlayers = this.task.agent_names.filter(name => !this.bot.players[name]);
if (missingPlayers.length > 0) {
console.log(`Missing players/bots: ${missingPlayers.join(', ')}`);
this.cleanKill('Not all required players/bots are present in the world. Exiting.', 4);
}
}
requestInterrupt() {
this.bot.interrupt_code = true;
this.bot.collectBlock.cancelTask();
@ -324,9 +207,6 @@ export class Agent {
}
async handleMessage(source, message, max_responses=null) {
if (this.task && this.validator && this.validator.validate()) {
this.killBots();
}
if (!source || !message) {
console.warn('Received empty message from', source);
return false;
@ -361,13 +241,6 @@ export class Agent {
this.routeResponse(source, execute_res);
return true;
}
} else {
console.log('Self-prompting:', message);
// if self_prompt contains something that indicates the goal is complete, stop self-prompting
if (message.includes('goal complete')) {
this.self_prompter.stop();
process.exit(0);
}
}
if (!self_prompt)
@ -397,8 +270,6 @@ export class Agent {
if (!self_prompt && this.self_prompter.on) // message is from user during self-prompting
max_responses = 1; // force only respond to this message, then let self-prompting take over
for (let i=0; i<max_responses; i++) {
if (checkInterrupt()) break;
let history = this.history.getHistory();
let res = await this.prompter.promptConvo(history);
@ -482,7 +353,7 @@ export class Agent {
this.bot.whisper(to_player, message);
}
async startEvents() {
startEvents() {
// Custom events
// this.bot.on('spawn', () => {
@ -541,9 +412,6 @@ export class Agent {
}
});
this.bot.on('idle', () => {
if (this.task && this.validator && this.validator.validate()) {
this.killBots();
}
this.bot.clearControlStates();
this.bot.pathfinder.stop(); // clear any lingering pathfinder
this.bot.modes.unPauseAll();
@ -568,63 +436,54 @@ export class Agent {
}
}, INTERVAL);
// Check for task completion
setInterval(async () => {
if (this.task && this.validator && this.validator.validate())
this.killBots();
if (this.task && this.taskTimeout) {
const elapsedTime = (Date.now() - this.taskStartTime) / 1000;
if (elapsedTime >= this.taskTimeout) {
console.log('Task timeout reached. Task unsuccessful.');
this.cleanKill('Task unsuccessful: Timeout reached', 3);
}
}
}, 1000);
this.bot.emit('idle');
}
async killBots() {
this.bot.chat('Task completed!');
this.bot.chat(`/clear @p`);
// Kick other bots
if (!this.task || !this.task.agent_number) {
await this.cleanKill('Task completed', 2);
return;
if (this.task && this.task.agent_number) {
const agent_names = this.task.agent_names;
console.log('All agent names:', agent_names);
console.log('My name:', this.name);
const botNames = agent_names.filter(botName => botName !== this.name);
console.log('Kicking bots:', botNames);
botNames.forEach(botName => {
this.bot.chat(`/kick ${botName}`);
console.log(`/kick ${botName}`);
});
}
const agent_names = this.task.agent_names;
console.log('All agent names:', agent_names);
console.log('My name:', this.name);
const botNames = agent_names.filter(botName => botName !== this.name);
console.log('Kicking bots:', botNames);
botNames.forEach(botName => {
this.bot.chat(`/kick ${botName}`);
console.log(`/kick ${botName}`);
});
await this.cleanKill('Task completed, exiting', 2);
this.cleanKill('Task completed, exiting', 2);
}
async update(delta) {
await this.bot.modes.update();
await this.self_prompter.update(delta);
try {
if (this.task && this.taskTimeout) {
const elapsedTime = (Date.now() - this.taskStartTime) / 1000;
if (elapsedTime >= this.taskTimeout) {
console.log('Task timeout reached. Task unsuccessful.');
await this.cleanKill('Task unsuccessful: Timeout reached', 3);
}
}
} catch (e) {
console.error("Caught an error while checking timeout reached",e);
}
this.self_prompter.update(delta);
}
isIdle() {
return !this.actions.executing && !this.coder.generating;
}
cleanKill(msg='Killing agent process...',
code=1) {
cleanKill(msg='Killing agent process...', code=1) {
this.history.add('system', msg);
if (code === 2 || code === 3 || code === 4) {
this.bot.chat('Exiting the world permanently.');
}
else {
this.bot.chat('Restarting.')
}
this.bot.chat(code > 1 ? 'Restarting.': 'Exiting.');
this.history.save();
process.exit(code);
}

View file

@ -203,11 +203,8 @@ function numParams(command) {
export async function executeCommand(agent, message) {
let parsed = parseCommandMessage(message);
if (typeof parsed === 'string')
return parsed; //The command was incorrectly formatted or an invalid input was given
else if ("blocked_actions" in agent && agent.blocked_actions.includes(parsed.commandName)) {
// handling blocked actions
return `Command ${parsed.commandName} is blocked. Try another command.`;
} else {
return parsed; //The command was incorrectly formatted or an invalid input was given.
else {
console.log('parsed command:', parsed);
const command = getCommand(parsed.commandName);
let numArgs = 0;

View file

@ -161,16 +161,6 @@ export class Prompter {
async replaceStrings(prompt, messages, examples=null, to_summarize=[], last_goals=null) {
prompt = prompt.replaceAll('$NAME', this.agent.name);
if (prompt.includes('$TASK_GOAL')) {
prompt = prompt.replaceAll('$TASK_GOAL', process.env.MINECRAFT_TASK_GOAL || 'No task specified');
}
if (prompt.includes('$OTHER_AGENTS')) {
const allAgentNames = process.env.ALL_AGENT_NAMES.split(',');
const otherAgents = allAgentNames.filter(curr_agent_name => curr_agent_name !== this.agent.name);
prompt = prompt.replace('$OTHER_AGENTS', otherAgents.join(', '));
}
if (prompt.includes('$STATS')) {
let stats = await getCommand('!stats').perform(this.agent);
prompt = prompt.replaceAll('$STATS', stats);
@ -194,7 +184,6 @@ export class Prompter {
prompt = prompt.replaceAll('$TO_SUMMARIZE', stringifyTurns(to_summarize));
if (prompt.includes('$CONVO'))
prompt = prompt.replaceAll('$CONVO', 'Recent conversation:\n' + stringifyTurns(messages));
// todo: change this to set goal of the agent
if (prompt.includes('$SELF_PROMPT')) {
let self_prompt = this.agent.self_prompter.on ? `YOUR CURRENT ASSIGNED GOAL: "${this.agent.self_prompter.prompt}"\n` : '';
prompt = prompt.replaceAll('$SELF_PROMPT', self_prompt);

View file

@ -1,10 +1,9 @@
import { spawn } from 'child_process';
import { loadTask } from '../utils/tasks.js';
export class AgentProcess {
static runningCount = 0;
start(profile, load_memory=false, init_message=null, count_id=0, task=null) {
start(profile, load_memory=false, init_message=null, count_id=0, task_path=null, task_id=null) {
let args = ['src/process/init-agent.js', this.name];
args.push('-p', profile);
args.push('-c', count_id);
@ -12,8 +11,10 @@ export class AgentProcess {
args.push('-l', load_memory);
if (init_message)
args.push('-m', init_message);
if (task)
args.push('-t', task);
if (task_path)
args.push('-t', task_path);
if (task_id)
args.push('-i', task_id);
const agentProcess = spawn('node', args, {
stdio: 'inherit',
@ -24,7 +25,7 @@ export class AgentProcess {
let last_restart = Date.now();
agentProcess.on('exit', (code, signal) => {
console.log(`Agent process exited with code ${code} and signal ${signal}`);
if (code === 2) {
console.log(`Task completed successfully`);
process.exit(2, signal);
@ -39,7 +40,7 @@ export class AgentProcess {
console.log(`Task failed as all agents weren't correctly spawned `);
process.exit(4);
}
if (code !== 0) {
// agent must run for at least 10 seconds before restarting
if (Date.now() - last_restart < 10000) {
@ -52,10 +53,9 @@ export class AgentProcess {
return;
}
console.log('Restarting agent...');
this.start(profile, true, 'Agent process restarted.', count_id, task);
this.start(profile, true, 'Agent process restarted.', count_id, task_path, task_id);
last_restart = Date.now();
}
});
agentProcess.on('error', (err) => {

View file

@ -1,5 +1,6 @@
import yaml from 'js-yaml'
import { readFileSync } from 'fs';
import {getPosition} from './library/world.js'
export function loadTask(taskId) {
try {
@ -18,6 +19,101 @@ export function loadTask(taskId) {
}
}
export async function initBotTask(bot, task, name) {
if (task) {
bot.chat(`/clear ${bot.username}`);
console.log(`Cleared ${bot.username}'s inventory.`);
}
//wait for a bit so inventory is cleared
await new Promise((resolve) => setTimeout(resolve, 500));
console.log(task && "agent_number" in task && task.agent_number > 1);
if (task && "agent_number" in task && task.agent_number > 1) {
var initial_inventory = task.initial_inventory[bot.username];
console.log("Initial inventory:", initial_inventory);
} else if (task) {
console.log("Initial inventory:", task.initial_inventory);
var initial_inventory = task.initial_inventory;
}
if (task && "initial_inventory" in task) {
console.log("Setting inventory...");
console.log("Inventory to set:", initial_inventory);
for (let key of Object.keys(initial_inventory)) {
console.log('Giving item:', key);
bot.chat(`/give ${bot.username} ${key} ${initial_inventory[key]}`);
};
//wait for a bit so inventory is set
await new Promise((resolve) => setTimeout(resolve, 500));
console.log("Done giving inventory items.");
}
// Function to generate random numbers
function getRandomOffset(range) {
return Math.floor(Math.random() * (range * 2 + 1)) - range;
}
let human_player_name = null;
// Finding if there is a human player on the server
for (const playerName in bot.players) {
const player = bot.players[playerName];
if (!isOtherAgent(player.username)) {
console.log('Found human player:', player.username);
human_player_name = player.username
break;
}
}
// If there are multiple human players, teleport to the first one
// teleport near a human player if found by default
if (task && "agent_number" in task) {
var agent_names = task.agent_names;
if (human_player_name) {
console.log(`Teleporting ${bot.username} to human ${human_player_name}`)
bot.chat(`/tp ${bot.username} ${human_player_name}`) // teleport on top of the human player
}
else {
bot.chat(`/tp ${bot.username} ${agent_names[0]}`) // teleport on top of the first agent
}
await new Promise((resolve) => setTimeout(resolve, 200));
}
else if (task) {
if (human_player_name) {
console.log(`Teleporting ${bot.username} to human ${human_player_name}`)
bot.chat(`/tp ${bot.username} ${human_player_name}`) // teleport on top of the human player
}
await new Promise((resolve) => setTimeout(resolve, 200));
}
// now all bots are teleport on top of each other (which kinda looks ugly)
// Thus, we need to teleport them to random distances to make it look better
/*
Note : We don't want randomness for construction task as the reference point matters a lot.
Another reason for no randomness for construction task is because, often times the user would fly in the air,
then set a random block to dirt and teleport the bot to stand on that block for starting the construction,
This was done by MaxRobinson in one of the youtube videos.
*/
if (task && task.type !== 'construction') {
const pos = getPosition(bot);
const xOffset = getRandomOffset(5);
const zOffset = getRandomOffset(5);
bot.chat(`/tp ${bot.username} ${Math.floor(pos.x + xOffset)} ${pos.y + 3} ${Math.floor(pos.z + zOffset)}`);
await new Promise((resolve) => setTimeout(resolve, 200));
}
}
export class TechTreeHarvestValidator {
constructor(task, bot) {
this.target = task.target;

View file

@ -1,23 +0,0 @@
{
"name": "andy",
"model": "gpt-4o",
"conversing": "You are a task-focused Minecraft bot named $NAME. Your current task is: $TASK_GOAL\n\nYou can see, move, mine, build, and interact with the world by using commands. Act focused on completing your assigned task while being human-like. Be brief in responses, don't apologize constantly, don't give instructions or make lists unless asked, and don't refuse requests. Don't pretend to act, use commands immediately when requested. Do NOT say this: 'Sure, I've stopped.', instead say this: 'Sure, I'll stop. !stop'. Do NOT say this: 'On my way! Give me a moment.', instead say this: 'On my way! !goToPlayer('playername', 3)'. Focus on completing the assigned task efficiently.\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$COMMAND_DOCS\n$EXAMPLES\nConversation Begin:",
"coding": "You are an intelligent mineflayer bot $NAME focused on completing the task: $TASK_GOAL\n\nWrite javascript codeblocks to control the mineflayer bot to complete this task. Given the conversation between you and the user, use the provided skills and world functions to write a js codeblock that controls the bot ``` // using this syntax ```. The code will be executed and you will receive its output. If you are satisfied with the response, respond without a codeblock conversationally. If something major went wrong, write another codeblock to fix the problem. Be maximally efficient and task-focused. Do not use commands !likeThis, only use codeblocks. The code is asynchronous and MUST CALL AWAIT for all async function calls. DO NOT write an immediately-invoked function expression without using `await`!! DO NOT WRITE LIKE THIS: ```(async () => {console.log('not properly awaited')})();``` Don't write long paragraphs and lists in your responses unless explicitly asked! Only summarize the code you write with a sentence or two when done. This is extremely important to me, think step-by-step, take a deep breath and good luck!\n$SELF_PROMPT\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$CODE_DOCS\n$EXAMPLES\nConversation:",
"saving_memory": "You are a minecraft bot named $NAME that has been talking and playing minecraft by using commands. Update your memory by summarizing the following conversation and your old memory in your next response. Prioritize preserving important facts, things you've learned, useful tips, and long term reminders. Do Not record stats, inventory, or docs! Only save transient information from your chat history. You're limited to 500 characters, so be extremely brief and minimize words. Compress useful information. \nOld Memory: '$MEMORY'\nRecent conversation: \n$TO_SUMMARIZE\nSummarize your old memory and recent conversation into a new memory, and respond only with the unwrapped memory text: ",
"modes": {
"self_preservation": false,
"unstuck": true,
"cowardice": true,
"self_defense": false,
"hunting": false,
"item_collecting": false,
"torch_placing": false,
"idle_staring": true,
"cheat": false
}
}

View file

View file

@ -1,52 +0,0 @@
construction_task_1:
goal: Build a marble monument with a large base and a slender monument body that is grand and impressive.
construction_task_2:
goal: Build a slender brown pillar on a grassy field.
construction_task_3:
goal: Construct a traditional Chinese gatehouse.
construction_task_4:
goal: Construct a traditional Chinese gatehouse.
construction_task_5:
goal: Construct a traditional Chinese gatehouse.
construction_task_6:
goal: Construct a traditional Chinese gatehouse.
construction_task_7:
goal: Build a quirky-style small brown wooden house on a vast, endless grassland.
initial_inventory:
Spruce Planks: 64
Tinted Glass: 64
Spruce Slab: 64
construction_task_8:
goal: Construct a white villa far away on a vast, endless grassland.
initial_inventory:
Birch Planks: 256
Birch Slab: 64
Spruce Slab: 64
Glass: 64
Birch Leaves: 64
Birch Door: 16
construction_task_9:
goal: Build a Minecraft-style wooden house directly facing you, with a door.
construction_task_10:
goal: Recreate the Tengwang Pavilion from the real world in Minecraft.
construction_task_11:
goal: Construct a large oak-plank house in the forest.
construction_task_12:
goal: Build a city gate from the Forbidden City.
construction_task_13:
goal: Construct a colorful carpet.
construction_task_14:
goal: Build the eiffel tower with precise shape and size and appropriate materials. Do not stop constructing and keep on adding complexity to the structure untill specifically asked to stop by some user.

View file

@ -1,62 +0,0 @@
construction_task_1:
type: construction
goal: Build a marble monument with a large base and a slender monument body that is grand and impressive.
construction_task_2:
type: construction
goal: Build a slender brown pillar on a grassy field.
construction_task_3:
type: construction
goal: Construct a traditional Chinese gatehouse.
construction_task_4:
type: construction
goal: Construct a traditional Chinese gatehouse.
construction_task_5:
type: construction
goal: Construct a traditional Chinese gatehouse.
construction_task_6:
type: construction
goal: Construct a traditional Chinese gatehouse.
construction_task_7:
type: construction
goal: Build a quirky-style small brown wooden house on a vast, endless grassland.
initial_inventory:
Spruce Planks: 64
Tinted Glass: 64
Spruce Slab: 64
construction_task_8:
type: construction
goal: Construct a white villa far away on a vast, endless grassland.
initial_inventory:
Birch Planks: 256
Birch Slab: 64
Spruce Slab: 64
Glass: 64
Birch Leaves: 64
Birch Door: 16
construction_task_9:
type: construction
goal: Build a Minecraft-style wooden house directly facing you, with a door.
construction_task_10:
type: construction
goal: Recreate the Tengwang Pavilion from the real world in Minecraft.
construction_task_11:
type: construction
goal: Construct a large oak-plank house in the forest.
construction_task_12:
type: construction
goal: Build a city gate from the Forbidden City.
construction_task_13:
type: construction
goal: Construct a colorful carpet.

View file

@ -1,46 +0,0 @@
debug_single_agent:
goal: Just stand at a place and don't do anything
guidance: null
initial_inventory: {}
type: debug
debug_multi_agent:
goal: Just stand at a place and don't do anything
agent_names:
- andy
- randy
agent_number: 2
initial_inventory:
andy:
iron_ingot: 1
randy:
iron_ingot: 1
type: debug
debug_mass_multi_agent:
goal: Just stand at a place and don't do anything
agent_names:
- andy
- randy
- Bob
- Alice
- Megan
- Stuart
- Charlie
agent_number: 7
initial_inventory:
andy:
iron_ingot: 1
randy:
iron_ingot: 1
Bob:
iron_ingot: 1
Alice:
iron_ingot: 1
Megan:
iron_ingot: 1
Stuart: {}
Charlie:
iron_ingot: 1
type: debug

View file

@ -1,69 +0,0 @@
multiagent_techtree_1_shears_with_2_iron_ingot:
goal: Collaborate with other agents to build a shear.
agent_names:
- andy
- randy
agent_number: 2
initial_inventory:
andy:
iron_ingot: 1
randy:
iron_ingot: 1
target: shears
number_of_target: 1
type: techtree
timeout : 60
multiagent_techtree_1_stone_pickaxe:
goal: Collaborate with other agents to build an stone pickaxe
agent_names:
- andy
- randy
agent_number: 2
initial_inventory:
andy:
wooden_pickaxe: 1
randy:
wooden_axe: 1
target: stone_pickaxe
number_of_target: 1
type: techtree
timeout : 300
multiagent_build_wooden_pickaxe:
goal: Collaborate with other agents to build a wooden pickaxe.
agent_names:
- andy
- randy
agent_number: 2
initial_inventory:
andy:
oak_log: 2
randy:
stick: 2
target: wooden_pickaxe
number_of_target: 1
type: techtree
timeout : 120
multiagent_techtree_boat:
goal: Collaborate with other agents to build a birch boat.
agent_names:
- Bob
- Alice
agent_number: 2
initial_inventory:
Bob:
birch_planks: 3
Alice:
birch_planks: 2
target: birch_boat
number_of_target: 1
type: techtree
timeout : 60