chore: reliability

This commit is contained in:
RainbowBird
2025-01-10 00:10:22 +08:00
parent a14e20b30f
commit 3d0a07f0bd
4 changed files with 69 additions and 24 deletions
+17 -7
View File
@@ -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) {
+22 -15
View File
@@ -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
}
}
}
}