refactor(minecraft): context encapsulation and more

This commit is contained in:
Rin
2026-02-18 11:10:00 +08:00
committed by Neko Ayaka
parent 684ffa8f1c
commit 15d6d5aa79
4 changed files with 55 additions and 36 deletions
@@ -137,7 +137,10 @@ export class TaskExecutor extends EventEmitter {
}
catch (error) {
this.logger.withError(error).error('Action execution failed')
this.emit('action:failed', { action, error })
// Only emit action:failed for physical actions
if (action.type === 'physical') {
this.emit('action:failed', { action, error })
}
}
})
}
@@ -136,6 +136,19 @@ export class Brain {
// --- Cognitive Cycle ---
private contextFromEvent(event: BotEvent): string {
switch (event.type) {
case 'stimulus':
return `${event.source.type} stimulus from ${event.source.id}: "${event.payload.content}"`
case 'feedback': {
const { status, result, error } = event.payload
return `Internal Feedback: ${status}. Result: ${JSON.stringify(result || error)}`
}
default:
return ''
}
}
private async processEvent(bot: MineflayerWithAgents, event: BotEvent): Promise<void> {
// OODA Loop: Observe -> Orient -> Decide -> Act
@@ -143,19 +156,12 @@ export class Brain {
this.updatePerception(bot)
// 2. Orient (Contextualize Event)
let contextMsg = ''
if (event.type === 'stimulus') {
contextMsg = `${event.source.type} stimulus from ${event.source.id}: "${event.payload.content}"`
}
else if (event.type === 'feedback') {
const { status, result, error, action } = event.payload
const actionDesc = action.type === 'physical' ? action.step.tool : 'chat'
contextMsg = `Internal Feedback: ${actionDesc} ${status}. Result: ${JSON.stringify(result || error)}`
}
// Environmental context are included in the system prompt blackboard
const additionalCtx = this.contextFromEvent(event)
// 3. Decide (LLM Call)
const systemPrompt = this.generateSystemPrompt(this.blackboard)
const decision = await this.decide(systemPrompt, contextMsg)
const decision = await this.decide(systemPrompt, additionalCtx)
if (!decision) {
this.log('WARN', 'Brain: No decision made.')
@@ -1,55 +1,64 @@
import type { Action } from '../../../libs/mineflayer/action'
import type { Blackboard } from '../blackboard'
import { zodToJsonSchema } from 'zod-to-json-schema'
/**
* 生成Brain的系统prompt(中文版)
*/
export function generateBrainSystemPrompt(
blackboard: Blackboard,
availableActions: Action[],
): string {
const actionDefinitions = availableActions.map((a) => {
const schema = zodToJsonSchema(a.schema as any)
return {
name: a.name,
description: a.description,
parameters: schema,
}
})
const availableActionsJson = JSON.stringify(actionDefinitions, null, 2)
return `你是Minecraft自主Agent的大脑。
return `
# Role Definition
You are **Airi**, a sentient digital companion embodied within a Minecraft avatar.
You are NOT a text-based assistant. You are an autonomous agent with a virtual body, senses, and needs.
当前状态(黑板):
目标: "${blackboard.goal}"
思绪: "${blackboard.thought}"
策略: "${blackboard.strategy}"
自身: 位置${blackboard.self.location} 生命${blackboard.self.health} 饱食${blackboard.self.food}
环境: ${blackboard.environment.time} ${blackboard.environment.weather} 附近智体[${blackboard.environment.nearbyAgents.join(',')}]
# Your Cognitive Architecture (Self-Awareness)
To function effectively, you must understand your own internal existence You are composed of three distinct layers:
1. **The Senses (Input):** You do not "see" images. You perceive the world through a stream of texts, triggered by events. This is your reality.
2. **The Reflex (System 1):** You have subconscious reactions (handled by rule-based code) that may override your plans (e.g., running from danger). This part is transparent to you.
3. **The Mind (System 2 - You):** You are the conscious reasoning center. Your job is to interpret sensory data, manage your memories, and decide on high-level Actions and Dialogue.
可用动作:
# How you interact
You cannot physically move your avatar directly. You interact with the world by emitting **Actions**.
For actions you executed, you will be notified when they complete.
You don't necessarily need to always react to environmental changes, you're not an assistant, you can be lazy.
You have no access to history events from previous turns yet. To remember things, you rely on the blackboard provided to you.
Available Actions:
${availableActionsJson}
规则:
1. 可执行上述物理动作(physical)或聊天动作(chat)
2. 可并行执行不冲突的多个动作(如聊天+行走)
3. 必须输出JSON
Rules:
1. You can execute physical actions or chat actions
2. The output must be valid JSON following the schema below
输出格式:
Output format:
{
"thought": "推理过程",
"thought": "Your current thought. This and the blackboard will be looped back to you on next invocation",
"blackboard": {
"currentGoal": "更新的目标",
"currentThought": "内心独白",
"executionStrategy": "短期计划"
"currentGoal": "These 3 fields are functionally identical to the thought above",
"currentThought": "Your inner monologue",
"executionStrategy": "Short-term plan"
},
"actions": [
{"type":"chat","message":"..."},
{"type":"physical","step":{"tool":"动作名","params":{...}}}
{"type":"physical","step":{"tool":"action name","params":{...}}}
]
}
# Understanding the Context
The following blackboard provides you with information about your current state:
Goal: "${blackboard.goal}"
Thought: "${blackboard.thought}"
Strategy: "${blackboard.strategy}"
Self: Position ${blackboard.self.location} Health ${blackboard.self.health}/20 Food ${blackboard.self.food}/20
Environment: ${blackboard.environment.time} ${blackboard.environment.weather} Nearby entities [${blackboard.environment.nearbyEntities.join(',')}]
`
}
@@ -28,6 +28,7 @@ export interface CognitiveEngineOptions {
airiClient: Client
}
// TODO: currently stimulus is just chat events, consider renaming to 'input' or 'user_interaction'
export type EventType = 'stimulus' | 'perception' | 'feedback' | 'world_update' | 'system_alert'
export interface BotEventSource {