fix(minecraft): clamp unsolicited chat and ignore harness wrappers

This commit is contained in:
Rin
2026-02-18 11:14:38 +08:00
committed by Neko Ayaka
parent 9e31bf33d5
commit 80aa5ba66d
3 changed files with 48 additions and 0 deletions
@@ -31,6 +31,7 @@ export const actionsList: Action[] = [
execution: 'sync',
schema: z.object({
message: z.string().describe('The message to send in chat.'),
feedback: z.boolean().default(false).describe('Whether to emit FEEDBACK for this chat action. Keep false for normal conversation to avoid feedback loops.'),
}),
perform: mineflayer => (message: string): string => {
mineflayer.bot.chat(message)
@@ -46,6 +46,8 @@ export class Brain {
private currentCancellationToken: CancellationToken | undefined
private giveUpUntil = 0
private giveUpReason: string | undefined
private lastHumanChatAt = 0
private botUsername = ''
private lastContextView: string | undefined
private conversationHistory: Message[] = []
@@ -55,6 +57,7 @@ export class Brain {
public init(bot: MineflayerWithAgents): void {
this.deps.logger.log('INFO', 'Brain: Initializing stateful core...')
this.botUsername = bot.bot.username
// Perception Handler
this.deps.eventBus.subscribe<PerceptionSignal>('conscious:signal:*', (event: TracedEvent<PerceptionSignal>) => {
@@ -70,6 +73,10 @@ export class Brain {
this.deps.taskExecutor.on('action:completed', async ({ action, result }) => {
this.deps.logger.log('INFO', `Brain: Action completed: ${action.tool}`)
if (action.tool === 'chat' && action.params?.feedback !== true) {
return
}
if (action.tool === 'giveUp') {
const secondsRaw = Number(action.params?.cooldown_seconds ?? 45)
const cooldownSeconds = Number.isFinite(secondsRaw) ? Math.min(600, Math.max(10, Math.floor(secondsRaw))) : 45
@@ -148,6 +155,7 @@ export class Brain {
// --- Cognitive Cycle ---
private async processEvent(bot: MineflayerWithAgents, event: BotEvent): Promise<void> {
this.updateHumanChatTimestamp(event)
this.resumeFromGiveUpIfNeeded(event)
if (this.shouldSuppressDuringGiveUp(event))
return
@@ -254,6 +262,10 @@ export class Brain {
this.deps.taskExecutor.getAvailableActions(),
{ event, snapshot: snapshot as unknown as Record<string, unknown> },
async (action: ActionInstruction) => {
if (action.tool === 'chat' && !this.shouldAllowChatForEvent(event, snapshot.self.health)) {
return 'Chat suppressed: no direct user prompt for chat this turn'
}
const actionDef = actionDefs.get(action.tool)
const isPhysicalAction = action.tool !== 'skip' && !actionDef?.readonly
@@ -362,4 +374,35 @@ export class Brain {
this.giveUpUntil = 0
this.giveUpReason = undefined
}
private shouldAllowChatForEvent(event: BotEvent, health: number): boolean {
if (health <= 8)
return true
if (event.type !== 'perception')
return Date.now() - this.lastHumanChatAt <= 45000 && event.type === 'feedback'
const signal = event.payload as PerceptionSignal
if (signal.type === 'chat_message') {
const speaker = typeof (signal.metadata as any)?.username === 'string'
? String((signal.metadata as any).username)
: signal.sourceId
if (speaker === this.botUsername)
return false
return true
}
return false
}
private updateHumanChatTimestamp(event: BotEvent): void {
if (event.type !== 'perception')
return
const signal = event.payload as PerceptionSignal
if (signal.type !== 'chat_message')
return
this.lastHumanChatAt = Date.now()
}
}
@@ -143,5 +143,9 @@ Examples:
- **Handling Feedback**: When you perform an action, you will see a \`[FEEDBACK]\` message in the history later with the result. Use this to verify success.
- **Tool Choice**: If a dedicated tool exists for a task, use it.
- **Skip Rule**: If you call \`skip()\`, do not call any other tool in the same turn.
- **Chat Discipline**: Do not send proactive small-talk. Use \`chat\` only when replying to a player chat, reporting meaningful task progress/failure, or urgent safety status.
- **No Harness Replies**: Never treat \`[PERCEPTION]\`, \`[FEEDBACK]\`, or other system wrappers as players. Only reply with \`chat\` to actual player \`chat_message\` events.
- **No Self Replies**: Never reply to your own previous bot messages.
- **Chat Feedback**: \`chat\` feedback is optional; keep \`feedback: false\` for normal conversation. Use \`feedback: true\` only when your next step explicitly needs the chat acknowledgement in history.
`
}