chore: reliability
This commit is contained in:
@@ -3,8 +3,9 @@ import type { MineflayerPlugin } from '../libs/mineflayer/plugin'
|
||||
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { assistant, system, user } from 'neuri/openai'
|
||||
import { toRetriable } from 'src/utils/reliability'
|
||||
import { formBotChat } from '../libs/mineflayer/message'
|
||||
import { genActionAgentPrompt } from '../prompts/agent'
|
||||
import { genActionAgentPrompt, genStatusPrompt } from '../prompts/agent'
|
||||
|
||||
export function LLMAgent(options: { agent: Neuri }): MineflayerPlugin {
|
||||
return {
|
||||
@@ -19,17 +20,21 @@ export function LLMAgent(options: { agent: Neuri }): MineflayerPlugin {
|
||||
const onChat = formBotChat(bot.username, async (username, message) => {
|
||||
logger.withFields({ username, message }).log('Chat message received')
|
||||
|
||||
const statusPrompt = await genStatusPrompt(bot)
|
||||
bot.memory.chatHistory.push(system(statusPrompt))
|
||||
bot.memory.chatHistory.push(user(`${username}: ${message}`))
|
||||
|
||||
// logger.withFields({ chatHistory: bot.memory.chatHistory }).log('chatHistory')
|
||||
logger.withFields({ statusPrompt }).log('statusPrompt')
|
||||
|
||||
const content = await agent.handleStateless([...bot.memory.chatHistory], async (c) => {
|
||||
logger.log('thinking...')
|
||||
|
||||
try {
|
||||
const handleCompletion = async (c: any): Promise<string> => {
|
||||
const completion = await c.reroute('action', c.messages, { model: 'openai/gpt-4o-mini' }) || { error: { message: 'Unknown error' } }
|
||||
if (!completion || 'error' in completion) {
|
||||
logger.withFields(c).error('Completion')
|
||||
return
|
||||
// throw new Error(completion?.error?.message ?? 'Unknown error')
|
||||
throw new Error(completion?.error?.message ?? 'Unknown error')
|
||||
}
|
||||
|
||||
const content = await completion?.firstContent()
|
||||
@@ -38,9 +43,14 @@ export function LLMAgent(options: { agent: Neuri }): MineflayerPlugin {
|
||||
|
||||
return content
|
||||
}
|
||||
catch (e) {
|
||||
logger.errorWithError('failed to think of an action', e)
|
||||
}
|
||||
|
||||
const retirableHandler = toRetriable<any, string>(
|
||||
3, // retryLimit
|
||||
1000, // delayInterval in ms
|
||||
handleCompletion,
|
||||
)
|
||||
|
||||
return await retirableHandler(c)
|
||||
})
|
||||
|
||||
if (content) {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import type { Mineflayer } from '../libs/mineflayer'
|
||||
import { listInventory } from '../skills/actions/inventory'
|
||||
|
||||
export function genSystemBasicPrompt(botName: string): string {
|
||||
return `You are a playful Minecraft bot named ${botName} that can converse with players, see, move,
|
||||
mine, build, and interact with the world by using commands.`
|
||||
}
|
||||
|
||||
export function genActionAgentPrompt(bot: Mineflayer): string {
|
||||
export function genActionAgentPrompt(mineflayer: Mineflayer): string {
|
||||
// ${ctx.prompt.selfPrompt}
|
||||
return `${genSystemBasicPrompt(bot.username)}
|
||||
|
||||
return `${genSystemBasicPrompt(mineflayer.username)}
|
||||
|
||||
Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in
|
||||
your responses, don't apologize constantly, don't give instructions or make lists unless
|
||||
@@ -16,27 +18,32 @@ asked, and don't refuse requests.
|
||||
Do not use any emojis. Just call the function given you if needed.
|
||||
|
||||
If I command you 'stop', then call the 'stop' function.
|
||||
|
||||
I will give you the following information:
|
||||
${bot.status.toOneLiner()}
|
||||
`
|
||||
|
||||
/**
|
||||
* Summarized memory: '${ctx.memory.getSummary()}'
|
||||
$STATS
|
||||
$INVENTORY
|
||||
$COMMAND_DOCS
|
||||
$EXAMPLES
|
||||
*/
|
||||
}
|
||||
|
||||
export function genQueryAgentPrompt(bot: Mineflayer): string {
|
||||
export async function genStatusPrompt(mineflayer: Mineflayer): Promise<string> {
|
||||
const inventory = await listInventory(mineflayer)
|
||||
const inventoryStr = inventory.map(item => `${item.name} x ${item.count}`).join(', ')
|
||||
const itemInHand = `${inventory[0].name} x ${inventory[0].count}` // TODO: mock
|
||||
|
||||
return `I will give you the following information:
|
||||
${mineflayer.status.toOneLiner()}
|
||||
|
||||
Inventory:
|
||||
${inventoryStr}
|
||||
|
||||
Item in hand:
|
||||
${itemInHand}
|
||||
`
|
||||
}
|
||||
|
||||
export function genQueryAgentPrompt(mineflayer: Mineflayer): string {
|
||||
const prompt = `You are a helpful assistant that asks questions to help me decide the next immediate
|
||||
task to do in Minecraft. My ultimate goal is to discover as many things as possible,
|
||||
accomplish as many tasks as possible and become the best Minecraft player in the world.
|
||||
|
||||
I will give you the following information:
|
||||
${bot.status.toOneLiner()}
|
||||
${mineflayer.status.toOneLiner()}
|
||||
`
|
||||
|
||||
return prompt
|
||||
|
||||
@@ -203,7 +203,7 @@ export async function giveToPlayer(
|
||||
*/
|
||||
export async function listInventory(mineflayer: Mineflayer): Promise<{ name: string, count: number }[]> {
|
||||
const items = await mineflayer.bot.inventory.items()
|
||||
sayItems(mineflayer, items)
|
||||
// sayItems(mineflayer, items)
|
||||
|
||||
return items.map(item => ({
|
||||
name: item.name,
|
||||
@@ -228,7 +228,7 @@ export async function sayItems(mineflayer: Mineflayer, items: Array<Item> | null
|
||||
mineflayer.bot.chat(`My inventory contains: ${output}`)
|
||||
}
|
||||
else {
|
||||
mineflayer.bot.chat('My inventory is empty.`')
|
||||
mineflayer.bot.chat('My inventory is empty.')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { sleep } from './helper'
|
||||
|
||||
/**
|
||||
* Returns a retirable anonymous function with configured retryLimit and delayInterval
|
||||
*
|
||||
* @param retryLimit Number of retry attempts
|
||||
* @param delayInterval Delay between retries in milliseconds
|
||||
* @param func Function to be called
|
||||
* @returns A wrapped function with the same signature as func
|
||||
*/
|
||||
export function toRetriable<A, R>(retryLimit: number, delayInterval: number, func: (...args: A[]) => Promise<R>): (...args: A[]) => Promise<R> {
|
||||
let retryCount = 0
|
||||
return async function (args: A): Promise<R> {
|
||||
try {
|
||||
return await func(args)
|
||||
}
|
||||
catch (err) {
|
||||
if (retryCount < retryLimit) {
|
||||
retryCount++
|
||||
await sleep(delayInterval)
|
||||
return await toRetriable(retryLimit, delayInterval, func)(args)
|
||||
}
|
||||
else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user