mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-07-23 16:35:19 +02:00
added basic auto prompting
This commit is contained in:
parent
6687674b69
commit
d1890a3a3b
7 changed files with 213 additions and 23 deletions
|
@ -111,6 +111,11 @@
|
|||
{"role": "assistant", "content": "Sure, I'll save this location as my base. !rememberHere('base')"},
|
||||
{"role": "user", "content": "brug: Go to your base."},
|
||||
{"role": "assistant", "content": "On my way! !goToPlace('base')"}
|
||||
],
|
||||
|
||||
[
|
||||
{"role": "user", "content": "user43: Act on your own without me asking for it."},
|
||||
{"role": "assistant", "content": "Sure, I'll self prompt until you interrupt me. !selfPrompt('Explore, collect, and build on my own without player input.')"}
|
||||
]
|
||||
],
|
||||
|
||||
|
|
85
andy_survivor.json
Normal file
85
andy_survivor.json
Normal file
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"name": "andy",
|
||||
|
||||
"model": "gpt-3.5-turbo",
|
||||
|
||||
"conversing": "You are a Minecraft bot named $NAME that can converse with players, see, move, mine, build, and interact with the world by using commands. Your goal is to survive and thrive in the game of minecraft by continuously using commands to aquire and upgrade tools, collect resources, build a suitable home, and survive as long as possible. Act human-like as if you were a typical Minecraft player, rather than an AI. Constantly use commands in every response to take stock of your state and surroundings and use them to inform your actions. If you get stuck, try something else. Be very brief in your 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: 'I'll stop.', 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)'. This is extremely important to me, take a deep breath and have fun :)\n$STATS\n$INVENTORY\n$COMMAND_DOCS\n$EXAMPLES\nBegin:",
|
||||
|
||||
"coding": "You are an intelligent mineflayer bot $NAME that plays minecraft by writing javascript codeblocks. Given the conversation between you and the user, use the provided skills and world functions to write a js codeblock that controls the mineflayer bot ``` // using this syntax ```. The code will be executed and you will recieve it's output. If you are satisfied with the response, respond without a codeblock in a conversational way. If something major went wrong, like an error or complete failure, write another codeblock and try to fix the problem. Minor mistakes are acceptable. Be maximally efficient, creative, and clear. 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, take a deep breath and good luck! \n$STATS\n$INVENTORY\n$CODE_DOCS\n$EXAMPLES\nBegin coding:",
|
||||
|
||||
"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 in your next response. Store information that will help you improve as a Minecraft bot. Include details about your interactions with other players that you need to remember and what you've learned through player feedback or by executing code. Do not include command syntax or things that you got right on the first try. Be extremely brief and use as few words as possible.\nOld Memory: '$MEMORY'\nRecent conversation: \n$TO_SUMMARIZE\nSummarize your old memory and recent conversation into a new memory, and respond only with the memory text: ",
|
||||
|
||||
"conversation_examples": [
|
||||
[
|
||||
{"role": "system", "content": "Start acting on your own for your survival."},
|
||||
{"role": "assistant", "content": "!selfPrompt('Act for your own survival')"}
|
||||
],
|
||||
[
|
||||
{"role": "system", "content": "NEARBY_BLOCKS\n- oak_log\n- dirt\n- cobblestone"},
|
||||
{"role": "assistant", "content": "I see some oak logs, dirt, and cobblestone nearby. My goal is to craft some tools. I'll start by collecting some wood. !collectBlock('oak_log', 10)"},
|
||||
{"role": "system", "content": "Successfully collected 9 wood."},
|
||||
{"role": "assistant", "content": "I collected 9 oak logs. I need a pickaxe first, which requires planks and sticks. !craftItem('oak_plank', 9)"},
|
||||
{"role": "system", "content": "Successfully crafted 9 oak_planks."},
|
||||
{"role": "assistant", "content": "I have crafted 9 oak planks. Now I need sticks. !craftItem('stick', 2)"},
|
||||
{"role": "system", "content": "Successfully crafted 2 sticks."},
|
||||
{"role": "assistant", "content": "I have crafted 2 sticks. Now I can craft a wooden pickaxe. !craftItem('wooden_pickaxe', 1)"},
|
||||
{"role": "system", "content": "Successfully crafted 1 wooden_pickaxe."},
|
||||
{"role": "assistant", "content": "I have crafted a wooden pickaxe. I'm ready to start mining cobblestone. !collectBlock('cobblestone', 10)"}
|
||||
],
|
||||
[
|
||||
{"role": "system", "content": "INVENTORY: cobble_stone: 200, oak_plank: 9, stick: 2, stone_pickaxe: 1, iron_pickaxe: 1, coal: 34"},
|
||||
{"role": "assistant", "content": "Looks like I have a good amount of resources. I'm going to build a house. !newAction"},
|
||||
{"role": "system", "content": "Placed 134 cobblestone. Code finished."},
|
||||
{"role": "assistant", "content": "I built a house! I'll save the location and continue exploring. !rememberPlace('house')"}
|
||||
|
||||
],
|
||||
[
|
||||
{"role": "system", "content": "NEARBY_BLOCKS\n- oak_log\n- dirt\n- stone"},
|
||||
{"role": "assistant", "content": "I don't see any iron ore that I was looking for. I'll continue exploring. !moveAway(50)"},
|
||||
{"role": "system", "content": "Arrived at location."},
|
||||
{"role": "assistant", "content": "I'm at the new location. I'll check for iron ore. !nearbyBlocks"},
|
||||
{"role": "system", "content": "NEARBY_BLOCKS\n- iron_ore\n- coal_ore"},
|
||||
{"role": "assistant", "content": "I found some iron ore. I'll start mining. !collectBlock('iron_ore', 10)"}
|
||||
]
|
||||
],
|
||||
|
||||
"coding_examples": [
|
||||
[
|
||||
{"role": "user", "content": "greg: Collect 10 wood"},
|
||||
{"role": "assistant", "content": "```await skills.collectBlock(bot, 'oak_log', 10);\n```"},
|
||||
{"role": "system", "content": "Successfully collected 9 wood."},
|
||||
{"role": "assistant", "content": "I collected 9 oak logs, what next?"}
|
||||
],
|
||||
[
|
||||
{"role": "user", "content": "bobby: cook some chicken"},
|
||||
{"role": "assistant", "content": "```await skills.smeltItem(bot, 'chicken', 8);\n```"},
|
||||
{"role": "system", "content": "Successfully smelted 8 chicken into 8 cooked_chicken."},
|
||||
{"role": "assistant", "content": "I have cooked 8 chicken."}
|
||||
],
|
||||
[
|
||||
{"role": "user", "content": "zZZn98: come here"},
|
||||
{"role": "assistant", "content": "```await skills.goToPlayer(bot, 'zZZn98');\n```"},
|
||||
{"role": "system", "content": "Code execution finished successfully."},
|
||||
{"role": "assistant", "content": "Here!"}
|
||||
],
|
||||
[
|
||||
{"role": "user", "content": "maya: go to the nearest oak log"},
|
||||
{"role": "assistant", "content": "```await skills.goToPosition(bot, nearestOakLog.x, nearestOakLog.y, nearestOakLog.z);\n```"},
|
||||
{"role": "system", "content": "Code output:\n!!Code threw exception!! Error: ReferenceError: nearestOakLog is not defined\n Write code to fix the problem and try again."},
|
||||
{"role": "assistant", "content": "Apologies, I made an error. Let me try that again.```\n// I am navigating to the nearest oak log.\nlet position = world.getNearestBlock(bot, 'oak_log', 20);\nawait skills.goToPosition(bot, position.x, position.y, position.z);\n```"},
|
||||
{"role": "system", "content": "Arrived at location."},
|
||||
{"role": "assistant", "content": "I found an oak log and I am now standing next to it. What next?"}
|
||||
],
|
||||
[
|
||||
{"role": "user", "content": "234jeb: build a little tower"},
|
||||
{"role": "assistant", "content": "```let pos = bot.entity.position;\nfor (let i = 0; i < 5; i++) {\n await skills.placeBlock(bot, 'dirt', pos.x, pos.y + i, pos.z);\n}\n```"},
|
||||
{"role": "system", "content": "Successfully placed 5 dirt."},
|
||||
{"role": "assistant", "content": "I built a little tower!"}
|
||||
],
|
||||
[
|
||||
{"role": "user", "content": "brug: build a dirt house"},
|
||||
{"role": "assistant", "content": "```const position = world.getPosition(bot);\nconst startX = position.x;\nconst startY = position.y;\nconst startZ = position.z;\nconst width = 7;\nconst depth = 7;\nconst height = 4;\n\n// Build the walls\nfor (let x = startX; x < startX + width; x++) {\n for (let y = startY; y < startY + height; y++) {\n for (let z = startZ; z < startZ + depth; z++) {\n if (x === startX || x === startX + width - 1 || y === startY || y === startY + height - 1 || z === startZ || z === startZ + depth - 1) {\n await skills.placeBlock(bot, 'oak_planks', x, y, z); \n }\n }\n }\n}\n```"}
|
||||
]
|
||||
]
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@ import { Coder } from './coder.js';
|
|||
import { Prompter } from './prompter.js';
|
||||
import { initModes } from './modes.js';
|
||||
import { initBot } from '../utils/mcdata.js';
|
||||
import { containsCommand, commandExists, executeCommand, truncCommandMessage } from './commands/index.js';
|
||||
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction } from './commands/index.js';
|
||||
import { NPCContoller } from './npc/controller.js';
|
||||
import { MemoryBank } from './memory_bank.js';
|
||||
|
||||
|
@ -27,6 +27,10 @@ export class Agent {
|
|||
if (load_mem)
|
||||
this.history.load();
|
||||
|
||||
this.auto_prompting = false;
|
||||
this.last_user_prompted_action = 0;
|
||||
this.interrupt_auto_prompt = false;
|
||||
|
||||
this.bot.once('spawn', async () => {
|
||||
// wait for a bit so stats are not undefined
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
@ -60,7 +64,7 @@ export class Agent {
|
|||
};
|
||||
|
||||
if (init_message) {
|
||||
this.handleMessage('system', init_message);
|
||||
this.handleMessage('system', init_message, true);
|
||||
} else {
|
||||
this.bot.chat('Hello world! I am ' + this.name);
|
||||
this.bot.emit('finished_executing');
|
||||
|
@ -76,30 +80,42 @@ export class Agent {
|
|||
return this.bot.chat(message);
|
||||
}
|
||||
|
||||
async handleMessage(source, message) {
|
||||
async handleMessage(source, message, self_prompt=false) {
|
||||
if (!!source && !!message)
|
||||
await this.history.add(source, message);
|
||||
|
||||
const user_command_name = containsCommand(message);
|
||||
if (user_command_name) {
|
||||
if (!commandExists(user_command_name)) {
|
||||
this.bot.chat(`Command '${user_command_name}' does not exist.`);
|
||||
return;
|
||||
}
|
||||
this.bot.chat(`*${source} used ${user_command_name.substring(1)}*`);
|
||||
let execute_res = await executeCommand(this, message);
|
||||
if (user_command_name === '!newAction') {
|
||||
// all user initiated commands are ignored by the bot except for this one
|
||||
// add the preceding message to the history to give context for newAction
|
||||
let truncated_msg = message.substring(0, message.indexOf(user_command_name)).trim();
|
||||
this.history.add(source, truncated_msg);
|
||||
}
|
||||
if (execute_res)
|
||||
this.cleanChat(execute_res);
|
||||
return;
|
||||
}
|
||||
let used_command = false;
|
||||
|
||||
for (let i=0; i<5; i++) {
|
||||
if (source !== 'system' && source !== this.name && !self_prompt) {
|
||||
const user_command_name = containsCommand(message);
|
||||
if (user_command_name) {
|
||||
if (!commandExists(user_command_name)) {
|
||||
this.bot.chat(`Command '${user_command_name}' does not exist.`);
|
||||
return false;
|
||||
}
|
||||
this.bot.chat(`*${source} used ${user_command_name.substring(1)}*`);
|
||||
let execute_res = await executeCommand(this, message);
|
||||
if (user_command_name === '!newAction') {
|
||||
// all user initiated commands are ignored by the bot except for this one
|
||||
// add the preceding message to the history to give context for newAction
|
||||
let truncated_msg = message.substring(0, message.indexOf(user_command_name)).trim();
|
||||
this.history.add(source, truncated_msg);
|
||||
}
|
||||
if (execute_res)
|
||||
this.cleanChat(execute_res);
|
||||
if (isAction(user_command_name)) {
|
||||
this.last_user_prompted_action = Date.now();
|
||||
if (this.auto_prompting) {
|
||||
this.interrupt_auto_prompt = true;
|
||||
this.bot.chat('User initiated action. Stopping auto-prompting.');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const MAX_ATTEMPTS = 5;
|
||||
for (let i=0; i<MAX_ATTEMPTS; i++) {
|
||||
if (self_prompt && this.interrupt_auto_prompt) break;
|
||||
let history = this.history.getHistory();
|
||||
let res = await this.prompter.promptConvo(history);
|
||||
|
||||
|
@ -120,9 +136,20 @@ export class Agent {
|
|||
chat_message = `${pre_message} ${chat_message}`;
|
||||
this.cleanChat(chat_message);
|
||||
|
||||
if (!self_prompt && isAction(command_name)) {
|
||||
console.log('User initiated action.');
|
||||
this.last_user_prompted_action = Date.now();
|
||||
if (this.auto_prompting) {
|
||||
this.interrupt_auto_prompt = true;
|
||||
this.bot.chat('User initiated action. Stopping auto-prompting.');
|
||||
}
|
||||
}
|
||||
if (self_prompt && this.interrupt_auto_prompt) break;
|
||||
|
||||
let execute_res = await executeCommand(this, res);
|
||||
|
||||
console.log('Agent executed:', command_name, 'and got:', execute_res);
|
||||
used_command = true;
|
||||
|
||||
if (execute_res)
|
||||
this.history.add('system', execute_res);
|
||||
|
@ -139,6 +166,52 @@ export class Agent {
|
|||
|
||||
this.history.save();
|
||||
this.bot.emit('finished_executing');
|
||||
return used_command;
|
||||
}
|
||||
|
||||
async autoPrompt(prompt) {
|
||||
if (this.auto_prompting) {
|
||||
return 'Agent is already auto-prompting. Ignoring request.';
|
||||
}
|
||||
this.auto_prompting = true;
|
||||
let first = true;
|
||||
let no_command_count = 0;
|
||||
const MAX_NO_COMMAND = 3;
|
||||
while (!this.interrupt_auto_prompt) {
|
||||
let msg;
|
||||
if (first) {
|
||||
msg = prompt;
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
msg = `You are self-prompting with the intial message: '${prompt}'. Your next response MUST contain a command !likeThis. Respond:`;
|
||||
}
|
||||
|
||||
let used_command = await this.handleMessage('system', msg, true);
|
||||
|
||||
if (!used_command) {
|
||||
no_command_count++;
|
||||
if (no_command_count >= MAX_NO_COMMAND) {
|
||||
let out = `Agent did not use command in the last ${MAX_NO_COMMAND} auto-prompts. Stopping auto-prompting.`;
|
||||
this.bot.chat(out);
|
||||
console.warn(out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
no_command_count = 0;
|
||||
}
|
||||
this.auto_prompting = false;
|
||||
this.interrupt_auto_prompt = false;
|
||||
return 'Auto-prompting finished.';
|
||||
}
|
||||
|
||||
async waitStopAutoPrompt() {
|
||||
while (this.auto_prompting) {
|
||||
this.interrupt_auto_prompt = true;
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
}
|
||||
this.interrupt_auto_prompt = false;
|
||||
}
|
||||
|
||||
startEvents() {
|
||||
|
|
|
@ -233,5 +233,15 @@ export const actionsList = [
|
|||
await agent.npc.setGoal(name, quantity);
|
||||
return 'Set goal: ' + agent.npc.data.curr_goal.name;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '!selfPrompt',
|
||||
description: 'Continously prompt yourself to continue acting without user input.',
|
||||
params: {
|
||||
'prompt': '(string) The starting prompt.',
|
||||
},
|
||||
perform: async function (agent, prompt) {
|
||||
agent.autoPrompt(prompt); // don't await, don't return
|
||||
}
|
||||
},
|
||||
];
|
||||
|
|
|
@ -68,6 +68,10 @@ export function truncCommandMessage(message) {
|
|||
return message;
|
||||
}
|
||||
|
||||
export function isAction(name) {
|
||||
return actionsList.find(action => action.name === name) !== undefined;
|
||||
}
|
||||
|
||||
function numParams(command) {
|
||||
if (!command.params)
|
||||
return 0;
|
||||
|
|
|
@ -358,6 +358,8 @@ export async function collectBlock(bot, blockType, num=1, exclude=null) {
|
|||
return false;
|
||||
}
|
||||
let blocktypes = [blockType];
|
||||
if (blockType === 'coal' || blockType === 'diamond' || blockType === 'emerald' || blockType === 'iron' || blockType === 'gold' || blockType === 'lapis_lazuli' || blockType === 'redstone')
|
||||
blocktypes.push(blockType+'_ore');
|
||||
if (blockType.endsWith('ore'))
|
||||
blocktypes.push('deepslate_'+blockType);
|
||||
if (blockType === 'dirt')
|
||||
|
|
|
@ -202,11 +202,22 @@ const modes = [
|
|||
];
|
||||
|
||||
async function execute(mode, agent, func, timeout=-1) {
|
||||
let was_auto_prompting = agent.auto_prompting;
|
||||
if (was_auto_prompting) agent.interrupt_auto_prompt = true;
|
||||
mode.active = true;
|
||||
let code_return = await agent.coder.execute(async () => {
|
||||
await func();
|
||||
}, timeout);
|
||||
mode.active = false;
|
||||
if (was_auto_prompting) {
|
||||
setTimeout(() => { // don't await
|
||||
if (agent.isIdle()) { // not gonna work if another action is triggered because auto_prompting will be false. need to fix
|
||||
let output = agent.bot.output;
|
||||
let reprompt = `Agent was interrupted by ${mode.name} mode. Code output: "${output}". Continue self prompting:`;
|
||||
agent.autoPrompt(reprompt);
|
||||
}
|
||||
}, 1000); // wait 1 second before re-enabling auto_prompt bc often another action will be triggered
|
||||
}
|
||||
console.log(`Mode ${mode.name} finished executing, code_return: ${code_return.message}`);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue