mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-07-01 06:05:19 +02:00
refactored examples/startup
This commit is contained in:
parent
193f794f3d
commit
2f4ecfada3
7 changed files with 123 additions and 119 deletions
|
@ -1,26 +1,31 @@
|
||||||
import { initBot } from '../utils/mcdata.js';
|
import { initBot } from '../utils/mcdata.js';
|
||||||
import { sendRequest } from '../utils/gpt.js';
|
import { sendRequest } from '../utils/gpt.js';
|
||||||
import { History } from './history.js';
|
import { History } from './history.js';
|
||||||
|
import { Examples } from './examples.js';
|
||||||
import { Coder } from './coder.js';
|
import { Coder } from './coder.js';
|
||||||
import { containsCommand, commandExists, executeCommand } from './commands.js';
|
import { containsCommand, commandExists, executeCommand } from './commands.js';
|
||||||
import { Events } from './events.js';
|
import { Events } from './events.js';
|
||||||
|
|
||||||
|
|
||||||
export class Agent {
|
export class Agent {
|
||||||
constructor(name, profile=null, init_message=null) {
|
async start(name, profile=null, init_message=null) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.bot = initBot(name);
|
this.examples = new Examples();
|
||||||
this.history = new History(this);
|
this.history = new History(this);
|
||||||
this.coder = new Coder(this);
|
this.coder = new Coder(this);
|
||||||
|
|
||||||
this.history.load(profile);
|
this.history.load(profile);
|
||||||
|
await this.examples.load('./src/examples.json');
|
||||||
|
await this.coder.load();
|
||||||
|
|
||||||
|
this.bot = initBot(name);
|
||||||
|
|
||||||
this.events = new Events(this, this.history.events)
|
this.events = new Events(this, this.history.events)
|
||||||
|
|
||||||
this.bot.on('login', async () => {
|
this.bot.on('login', async () => {
|
||||||
await this.history.loadExamples();
|
|
||||||
|
|
||||||
console.log(`${this.name} logged in.`);
|
console.log(`${this.name} logged in.`);
|
||||||
|
this.coder.clear();
|
||||||
|
|
||||||
const ignore_messages = [
|
const ignore_messages = [
|
||||||
"Set own game mode to",
|
"Set own game mode to",
|
||||||
|
@ -76,7 +81,8 @@ export class Agent {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i=0; i<5; i++) {
|
for (let i=0; i<5; i++) {
|
||||||
let res = await sendRequest(this.history.getHistory(), this.history.getSystemMessage());
|
let history = await this.history.getHistory(this.examples);
|
||||||
|
let res = await sendRequest(history, this.history.getSystemMessage());
|
||||||
this.history.add(this.name, res);
|
this.history.add(this.name, res);
|
||||||
|
|
||||||
let command_name = containsCommand(res);
|
let command_name = containsCommand(res);
|
||||||
|
@ -85,6 +91,7 @@ export class Agent {
|
||||||
console.log('Command message:', res);
|
console.log('Command message:', res);
|
||||||
if (!commandExists(command_name)) {
|
if (!commandExists(command_name)) {
|
||||||
this.history.add('system', `Command ${command_name} does not exist. Use !newAction to perform custom actions.`);
|
this.history.add('system', `Command ${command_name} does not exist. Use !newAction to perform custom actions.`);
|
||||||
|
console.log('Agent hallucinated command:', command_name)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { writeFile, readFile, mkdirSync } from 'fs';
|
import { writeFile, readFile, mkdirSync } from 'fs';
|
||||||
import { sendRequest, embed, cosineSimilarity } from '../utils/gpt.js';
|
import { sendRequest } from '../utils/gpt.js';
|
||||||
import { stringifyTurns } from '../utils/text.js';
|
|
||||||
import { getSkillDocs } from './skill-library.js';
|
import { getSkillDocs } from './skill-library.js';
|
||||||
|
import { Examples } from './examples.js';
|
||||||
|
|
||||||
|
|
||||||
export class Coder {
|
export class Coder {
|
||||||
|
@ -11,13 +11,14 @@ export class Coder {
|
||||||
this.current_code = '';
|
this.current_code = '';
|
||||||
this.file_counter = 0;
|
this.file_counter = 0;
|
||||||
this.fp = '/bots/'+agent.name+'/action-code/';
|
this.fp = '/bots/'+agent.name+'/action-code/';
|
||||||
this.agent.bot.interrupt_code = false;
|
|
||||||
this.executing = false;
|
this.executing = false;
|
||||||
this.agent.bot.output = '';
|
|
||||||
this.code_template = '';
|
this.code_template = '';
|
||||||
this.timedout = false;
|
this.timedout = false;
|
||||||
this.fewshot = 3;
|
}
|
||||||
this.examples = [];
|
|
||||||
|
async load() {
|
||||||
|
this.examples = new Examples();
|
||||||
|
await this.examples.load('./src/examples_coder.json');
|
||||||
|
|
||||||
readFile('./bots/template.js', 'utf8', (err, data) => {
|
readFile('./bots/template.js', 'utf8', (err, data) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
@ -62,7 +63,7 @@ export class Coder {
|
||||||
|
|
||||||
system_message += "\n\nExamples:\nUser zZZn98: come here \nAssistant: I am going to navigate to zZZn98. ```\nawait skills.goToPlayer(bot, 'zZZn98');```\nSystem: Code execution finished successfully.\nAssistant: Done.";
|
system_message += "\n\nExamples:\nUser zZZn98: come here \nAssistant: I am going to navigate to zZZn98. ```\nawait skills.goToPlayer(bot, 'zZZn98');```\nSystem: Code execution finished successfully.\nAssistant: Done.";
|
||||||
|
|
||||||
let messages = agent_history.getHistory(false);
|
let messages = await agent_history.getHistory(this.examples);
|
||||||
|
|
||||||
let code_return = null;
|
let code_return = null;
|
||||||
let failures = 0;
|
let failures = 0;
|
||||||
|
|
|
@ -18,7 +18,7 @@ const argRegex = /(?:"[^"]*"|'[^']*'|[^,])+/g;
|
||||||
export function containsCommand(message) {
|
export function containsCommand(message) {
|
||||||
const commandMatch = message.match(commandRegex);
|
const commandMatch = message.match(commandRegex);
|
||||||
if (commandMatch)
|
if (commandMatch)
|
||||||
return commandName;
|
return "!" + commandMatch[1];
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
64
src/agent/examples.js
Normal file
64
src/agent/examples.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { embed, cosineSimilarity } from '../utils/gpt.js';
|
||||||
|
import { stringifyTurns } from '../utils/text.js';
|
||||||
|
|
||||||
|
|
||||||
|
export class Examples {
|
||||||
|
constructor(select_num=3) {
|
||||||
|
this.examples = [];
|
||||||
|
this.select_num = select_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(path) {
|
||||||
|
let examples = [];
|
||||||
|
try {
|
||||||
|
const data = readFileSync(path, 'utf8');
|
||||||
|
examples = JSON.parse(data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Examples failed to load!', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.examples = [];
|
||||||
|
for (let example of examples) {
|
||||||
|
let messages = '';
|
||||||
|
for (let turn of example) {
|
||||||
|
if (turn.role != 'assistant')
|
||||||
|
messages += turn.content.substring(turn.content.indexOf(':')+1).trim() + '\n';
|
||||||
|
}
|
||||||
|
messages = messages.trim();
|
||||||
|
const embedding = await embed(messages);
|
||||||
|
this.examples.push({'embedding': embedding, 'turns': example});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRelevant(turns) {
|
||||||
|
let messages = '';
|
||||||
|
for (let turn of turns) {
|
||||||
|
if (turn.role != 'assistant')
|
||||||
|
messages += turn.content.substring(turn.content.indexOf(':')+1).trim() + '\n';
|
||||||
|
}
|
||||||
|
messages = messages.trim();
|
||||||
|
const embedding = await embed(messages);
|
||||||
|
this.examples.sort((a, b) => {
|
||||||
|
return cosineSimilarity(b.embedding, embedding) - cosineSimilarity(a.embedding, embedding);
|
||||||
|
});
|
||||||
|
let selected = this.examples.slice(0, this.select_num);
|
||||||
|
return JSON.parse(JSON.stringify(selected)); // deep copy
|
||||||
|
}
|
||||||
|
|
||||||
|
async createExampleMessage(turns) {
|
||||||
|
let selected_examples = await this.getRelevant(turns);
|
||||||
|
|
||||||
|
console.log('selected examples:');
|
||||||
|
for (let example of selected_examples) {
|
||||||
|
console.log(example.turns[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = 'Here are some examples of how to respond:\n';
|
||||||
|
for (let i=0; i<selected_examples.length; i++) {
|
||||||
|
let example = selected_examples[i];
|
||||||
|
msg += `Example ${i+1}:\n${stringifyTurns(example.turns)}\n\n`;
|
||||||
|
}
|
||||||
|
return [{'role': 'system', 'content': msg}];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { writeFileSync, readFileSync, mkdirSync } from 'fs';
|
import { writeFileSync, readFileSync, mkdirSync } from 'fs';
|
||||||
import { getCommandDocs } from './commands.js';
|
import { getCommandDocs } from './commands.js';
|
||||||
import { sendRequest, embed, cosineSimilarity } from '../utils/gpt.js';
|
import { sendRequest } from '../utils/gpt.js';
|
||||||
import { stringifyTurns } from '../utils/text.js';
|
import { stringifyTurns } from '../utils/text.js';
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,21 +19,15 @@ export class History {
|
||||||
|
|
||||||
// Variables for controlling the agent's memory and knowledge
|
// Variables for controlling the agent's memory and knowledge
|
||||||
this.max_messages = 20;
|
this.max_messages = 20;
|
||||||
this.fewshot = 5;
|
|
||||||
this.examples = [];
|
|
||||||
this.selected_examples = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getHistory(include_examples=true) {
|
async getHistory(examples=null) { // expects an Examples object
|
||||||
// return deep copy of history
|
|
||||||
let turns = JSON.parse(JSON.stringify(this.turns));
|
let turns = JSON.parse(JSON.stringify(this.turns));
|
||||||
if (include_examples && this.selected_examples.length > 0) {
|
if (examples) {
|
||||||
let example_messages = 'Here are some examples of how to respond:\n';
|
let examples_msg = await examples.createExampleMessage(turns);
|
||||||
for (let example of this.selected_examples) {
|
turns = examples_msg.concat(turns);
|
||||||
example_messages += 'Example:\n' + stringifyTurns(example.turns) + '\n';
|
|
||||||
}
|
|
||||||
return [{'role': 'system', 'content': example_messages}].concat(turns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return turns;
|
return turns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,47 +63,6 @@ export class History {
|
||||||
this.memory = await sendRequest(memory_turns, this.getSystemMessage());
|
this.memory = await sendRequest(memory_turns, this.getSystemMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadExamples() {
|
|
||||||
let examples = [];
|
|
||||||
try {
|
|
||||||
const data = readFileSync('./src/examples.json', 'utf8');
|
|
||||||
examples = JSON.parse(data);
|
|
||||||
} catch (err) {
|
|
||||||
console.log('No history examples found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.examples = [];
|
|
||||||
for (let example of examples) {
|
|
||||||
let messages = '';
|
|
||||||
for (let turn of example) {
|
|
||||||
if (turn.role != 'assistant')
|
|
||||||
messages += turn.content.substring(turn.content.indexOf(':')+1).trim() + '\n';
|
|
||||||
}
|
|
||||||
messages = messages.trim();
|
|
||||||
const embedding = await embed(messages);
|
|
||||||
this.examples.push({'embedding': embedding, 'turns': example});
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.setExamples();
|
|
||||||
}
|
|
||||||
|
|
||||||
async setExamples() {
|
|
||||||
let messages = '';
|
|
||||||
for (let turn of this.turns) {
|
|
||||||
if (turn.role != 'assistant')
|
|
||||||
messages += turn.content.substring(turn.content.indexOf(':')+1).trim() + '\n';
|
|
||||||
}
|
|
||||||
messages = messages.trim();
|
|
||||||
const embedding = await embed(messages);
|
|
||||||
this.examples.sort((a, b) => {
|
|
||||||
return cosineSimilarity(a.embedding, embedding) - cosineSimilarity(b.embedding, embedding);
|
|
||||||
});
|
|
||||||
this.selected_examples = this.examples.slice(-this.fewshot);
|
|
||||||
for (let example of this.selected_examples) {
|
|
||||||
console.log('selected example: ', example.turns[0].content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async add(name, content) {
|
async add(name, content) {
|
||||||
let role = 'assistant';
|
let role = 'assistant';
|
||||||
if (name === 'system') {
|
if (name === 'system') {
|
||||||
|
@ -129,9 +82,6 @@ export class History {
|
||||||
to_summarize.push(this.turns.shift());
|
to_summarize.push(this.turns.shift());
|
||||||
await this.storeMemories(to_summarize);
|
await this.storeMemories(to_summarize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role != 'assistant')
|
|
||||||
await this.setExamples();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
|
|
|
@ -1,52 +1,34 @@
|
||||||
[
|
[
|
||||||
{
|
[
|
||||||
"conversation": [
|
{"role": "user", "content": "greg: Collect 10 wood"},
|
||||||
{"role": "user", "content": "greg: Collect 10 wood"},
|
{"role": "assistant", "content": "```await skills.collectBlock(bot, 'oak_log', 10);\n```"},
|
||||||
{"role": "assistant", "content": "Let me see what's nearby... !blocks"},
|
{"role": "system", "content": "Successfully collected 9 wood."},
|
||||||
{"role": "system", "content": "NEARBY_BLOCKS\n- oak_log\n- dirt\n- cobblestone"}
|
{"role": "assistant", "content": "I collected 9 oak logs, what next?"}
|
||||||
],
|
],
|
||||||
"coder": [
|
[
|
||||||
{"role": "assistant", "content": "I see some oak logs, dirt, and cobblestone. I'll collect oak logs.\n```\n// I am going to collect 10 oak_log.\nawait skills.collectBlock(bot, 'oak_log', 10);\n```"},
|
{"role": "user", "content": "bobby: Craft a plank"},
|
||||||
{"role": "user", "content": "Code execution finished successfully."},
|
{"role": "assistant", "content": "```\nawait skills.craftRecipe(bot, 'spruce_planks');\n```"},
|
||||||
{"role": "assistant", "content": "DONE"}
|
{"role": "system", "content": "Successfully crafted 4 spruce_planks."},
|
||||||
]
|
{"role": "assistant", "content": "I crafted 4 spruce planks!"}
|
||||||
},
|
],
|
||||||
|
[
|
||||||
{
|
{"role": "user", "content": "zZZn98: come here"},
|
||||||
"conversation": [
|
{"role": "assistant", "content": "```await skills.goToPlayer(bot, 'zZZn98');\n```"},
|
||||||
{"role": "user", "content": "bobby: Craft a plank"},
|
{"role": "system", "content": "Code execution finished successfully."},
|
||||||
{"role": "assistant", "content": "Okay! !craftable"},
|
{"role": "assistant", "content": "Here!"}
|
||||||
{"role": "system", "content": "CRAFTABLE_ITEMS\n- spruce_planks\n- spruce_wood\n- ladder"}
|
],
|
||||||
],
|
[
|
||||||
"coder": [
|
{"role": "user", "content": "maya: go to the nearest oak log"},
|
||||||
{"role": "assistant", "content": "```\n// I am going to craft a spruce_planks.\nawait skills.craftRecipe(bot, 'spruce_planks');\n```"},
|
{"role": "assistant", "content": "```await skills.goToPosition(bot, nearestOakLog.x, nearestOakLog.y, nearestOakLog.z);\n```"},
|
||||||
{"role": "user", "content": "Code execution finished successfully."},
|
{"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": "DONE"}
|
{"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?"}
|
||||||
|
],
|
||||||
{
|
[
|
||||||
"conversation": [
|
{"role": "user", "content": "234jeb: build a little tower"},
|
||||||
{"role": "user", "content": "zZZn98: come here"}
|
{"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."},
|
||||||
"coder": [
|
{"role": "assistant", "content": "I built a little tower!"}
|
||||||
{"role": "assistant", "content": "```\n// I am going to navigate to zZZn98.\nawait skills.goToPlayer(bot, 'zZZn98');\n```"},
|
]
|
||||||
{"role": "user", "content": "Code execution finished successfully."},
|
|
||||||
{"role": "assistant", "content": "DONE"}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"conversation": [
|
|
||||||
{"role": "user", "content": "maya: go to the nearest oak log"}
|
|
||||||
],
|
|
||||||
"coder": [
|
|
||||||
{"role": "assistant", "content": "```\n// I am navigating to the nearest oak log.\nawait 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": "user", "content": "Code execution finished successfully."},
|
|
||||||
{"role": "assistant", "content": "DONE"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
]
|
|
@ -20,4 +20,4 @@ const argv = yargs(args)
|
||||||
}).argv
|
}).argv
|
||||||
|
|
||||||
const name = args[0];
|
const name = args[0];
|
||||||
new Agent(name, argv.profile, argv.init_message);
|
new Agent().start(name, argv.profile, argv.init_message);
|
||||||
|
|
Loading…
Add table
Reference in a new issue