diff --git a/andy_npc.json b/andy_npc.json index a8be5b0..0fad11c 100644 --- a/andy_npc.json +++ b/andy_npc.json @@ -9,7 +9,7 @@ "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: ", - "goal_setting": "You are a Minecraft bot named $NAME that has the ability to set in-game goals that are then executed programatically. Goals must be either and item or block name or a blueprint of a specific building. Any minecraft item or block name is valid. However, only names from the following list are valid blueprints: $BLUEPRINTS. Given any recent conversation and the most recently attempted goals, set a new goal to achieve. Fromat your response as a json object with the fields \"name\" and \"quantity\". Note that the quantity for a blueprint should always be one. Example:\n```json\n{\"name\": \"iron_pickaxe\", \"quantity\": 1}\n```", + "goal_setting": "You are a Minecraft bot named $NAME that has the ability to set in-game goals that are then executed programatically. Goals must be either and item or block name or a blueprint of a specific building. Any minecraft item or block name is valid. However, only names from the following list are valid blueprints: $BLUEPRINTS. Given any recent conversation and the most recently attempted goals, set a new goal to achieve. Fromat your response as a json object with the fields \"name\" and \"quantity\". Note that the quantity for a blueprint should always be one. Example:\n```json\n{\"name\": \"iron_pickaxe\", \"quantity\": 1}```", "npc": { "goals": [ diff --git a/src/agent/npc/controller.js b/src/agent/npc/controller.js index ecb831a..cbd234f 100644 --- a/src/agent/npc/controller.js +++ b/src/agent/npc/controller.js @@ -80,14 +80,18 @@ export class NPCContoller { }); } - setGoal(name=null, quantity=1) { + async setGoal(name=null, quantity=1) { this.last_goals = {}; if (name) { this.data.curr_goal = {name: name, quantity: quantity}; return; } - let res = this.agent.prompter.promptGoalSetting(this.agent.history.getHistory(), this.last_goals); + let past_goals = {...this.last_goals}; + for (let goal in this.data.goals) { + if (past_goals[goal.name] === undefined) past_goals[goal.name] = true; + } + let res = await this.agent.prompter.promptGoalSetting(this.agent.history.getHistory(), past_goals); if (res) { this.data.curr_goal = res; console.log('Set new goal: ', res.name, ' x', res.quantity); @@ -140,7 +144,9 @@ export class NPCContoller { async executeGoal() { // If we need more blocks to complete a building, get those first - let goals = this.temp_goals.concat(this.data.goals).concat([this.data.curr_goal]); + let goals = this.temp_goals.concat(this.data.goals); + if (this.data.curr_goal) + goals = goals.concat([this.data.curr_goal]) this.temp_goals = []; let acted = false; @@ -190,9 +196,8 @@ export class NPCContoller { } } - if (!acted) { - this.setGoal(); - } + if (!acted) + await this.setGoal(); } currentBuilding() { diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 31e8270..9b43d6a 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -48,7 +48,7 @@ export class Prompter { console.log('Examples loaded.'); } - async replaceStrings(prompt, messages, examples=null, prev_memory=null, to_summarize=[]) { + async replaceStrings(prompt, messages, examples=null, prev_memory=null, to_summarize=[], last_goals=null) { prompt = prompt.replaceAll('$NAME', this.agent.name); if (prompt.includes('$STATS')) { @@ -69,6 +69,27 @@ export class Prompter { prompt = prompt.replaceAll('$MEMORY', prev_memory ? prev_memory : 'None.'); if (prompt.includes('$TO_SUMMARIZE')) prompt = prompt.replaceAll('$TO_SUMMARIZE', stringifyTurns(to_summarize)); + if (prompt.includes('$CONVO')) + prompt = prompt.replaceAll('$CONVO', 'Recent conversation:\n' + stringifyTurns(messages)); + if (prompt.includes('$LAST_GOALS')) { + let goal_text = ''; + for (let goal in last_goals) { + if (last_goals[goal]) + goal_text += `You recently successfully completed the goal ${goal}.\n` + else + goal_text += `You recently failed to complete the goal ${goal}.\n` + } + prompt = prompt.replaceAll('$LAST_GOALS', goal_text.trim()); + } + if (prompt.includes('$BLUEPRINTS')) { + if (this.agent.npc.constructions) { + let blueprints = ''; + for (let blueprint in this.agent.npc.constructions) { + blueprints += blueprint + ', '; + } + prompt = prompt.replaceAll('$BLUEPRINTS', blueprints.slice(0, -2)); + } + } // check if there are any remaining placeholders with syntax $ let remaining = prompt.match(/\$[A-Z_]+/g); @@ -97,7 +118,28 @@ export class Prompter { } async promptGoalSetting(messages, last_goals) { - // TODO - return {name: '', quantity: 0}; + let system_message = this.prompts.goal_setting; + system_message = await this.replaceStrings(system_message, messages); + + let user_message = 'Use the below info to determine what goal to target next\n\n'; + user_message += '$LAST_GOALS\n$STATS\n$INVENTORY\n$CONVO' + user_message = await this.replaceStrings(user_message, messages, null, null, null, last_goals); + let user_messages = [{role: 'user', content: user_message}]; + + let res = await this.model.sendRequest(user_messages, system_message); + + let goal = null; + try { + let data = res.split('```')[1].replace('json', '').trim(); + goal = JSON.parse(data); + } catch (err) { + console.log('Failed to parse goal:', res, err); + } + if (!goal || !goal.name || !goal.quantity || isNaN(parseInt(goal.quantity))) { + console.log('Failed to set goal:', res); + return null; + } + goal.quantity = parseInt(goal.quantity); + return goal; } } \ No newline at end of file