Introduction
In the exploration path of AI companions, ECHO_loop represents a purely engineering-driven approach—adopting the React Agent Loop architecture to integrate LLM orchestration, game interaction, and memory management into an Electron desktop application.
Unlike ECHO_feeling's cognitive science architecture (seven-layer cognition, emotional system) and ECHO_CLAW's context-driven design, ECHO_loop's core is engineering practice first: using mature tech stacks and clear processes to achieve intelligent behavior.
Why React Agent Loop
Limitations of Traditional Approaches
In early Minecraft AI implementations, we tried two paths:
- Hard-coded Rule System: Define numerous conditional branches like "attack when seeing enemies", "run away when health is low". The problem is rigid behavior that cannot handle unexpected situations.
- Pure LLM Decision: Let LLM decide every step. Problems include slow response, high cost, and tendency to loop.
The Balance Point of React Agent
React Agent Loop (from the ReAct paper) provides a compromise:
Observe → Think → Act → Observe...
The key aspects of this loop:
- Visible Thinking Steps: LLM's reasoning process is explicit, easy to debug and intervene
- Interruptible Actions: Each action is an independent atomic operation, can adjust after failure
- Accumulating Context: Results of each loop feed into the next iteration's input
System Architecture
Overall Architecture
┌─────────────────────────────────────────────────────────────┐
│ Electron Desktop App │
├─────────────────────────────────────────────────────────────┤
│ Vue 3 Frontend │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Chat Panel │ │
│ │ • User input │ │
│ │ • AI response display │ │
│ │ • Action logs │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Memory Panel │ │
│ │ • Episodic memory browser │ │
│ │ • Semantic memory retrieval │ │
│ │ • Status statistics │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Settings │ │
│ │ • LLM config │ │
│ │ • Game server config │ │
│ │ • Behavior parameters │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ IPC Communication Layer │
│ • Renderer ↔ Main process communication │
│ • Event-driven: onMessage, onAction, onMemoryUpdate │
├─────────────────────────────────────────────────────────────┤
│ Node.js Main Process │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LangChain Agent (Tool-Calling) │ │
│ │ • ReAct loop implementation │ │
│ │ • Tool registration & invocation │ │
│ │ • Streaming output handling │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Mineflayer Bot Controller │ │
│ │ • Connect to Minecraft server │ │
│ │ • Listen to game events │ │
│ │ • Execute game operations │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Local Vector DB (sql.js) │ │
│ │ • Episodic memory storage │ │
│ │ • Vector embedding & retrieval │ │
│ │ • Encrypted persistence │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Tech Stack Selection
| Component | Technology | Reason for Selection |
|---|---|---|
| Runtime | Electron | Cross-platform desktop app, local storage support |
| Frontend | Vue 3 | Reactive UI, component-based development |
| Agent Framework | LangChain | Mature Tool-Calling Agent implementation |
| Game Interaction | Mineflayer | Node.js implementation of Minecraft protocol |
| Local Database | sql.js | Pure JavaScript SQLite, no external dependencies |
Core Flow Implementation
Engineering ReAct Loop
LangChain's Tool-Calling Agent is essentially an engineering implementation of ReAct. We extended it:
// Core loop structure
interface ReActLoop {
// 1. Observe: Collect game state
observe(): GameState
// 2. Think: LLM reasons next step
think(state: GameState, context: ConversationHistory): Promise<Action[]>
// 3. Act: Execute specific operations
act(actions: Action[]): Promise<ActionResult[]>
// 4. Reflect: Evaluate results, update memory
reflect(results: ActionResult[]): void
}Code Agent Mode
Traditional ReAct actions are single-step operations, but in complex environments like Minecraft, single-step operations are too slow. We introduced Code Agent Mode:
// AI-generated JavaScript code block
const generatedCode = `
// 1. Check for nearby wood
const nearbyTrees = bot.findBlocks({
matching: mcData.blocksByName.log.id,
maxDistance: 32
});
// 2. Move to nearest tree
if (nearbyTrees.length > 0) {
await bot.pathfinder.goto(
new GoalBlock(nearbyTrees[0].x, nearbyTrees[0].y, nearbyTrees[0].z)
);
}
// 3. Dig wood
await bot.dig(bot.blockAt(nearbyTrees[0]));
`;
// Execute in sandbox
await sandbox.execute(generatedCode);Advantages of this mode:
- Batch Operations: Generate multiple steps at once, reduce LLM calls
- Context Coherence: Complete logic within code block
- Debuggable: Generated code can be viewed and modified
node:vm Sandbox Isolation
Security is key for Code Agent. We use node:vm to create isolated sandbox:
import { VM } from 'vm2';
const sandbox = new VM({
timeout: 5000, // 5 second timeout
sandbox: {
bot: botProxy, // Proxy object, limit dangerous operations
mcData: mcData,
console: safeConsole // Safe console, output to log
}
});
// Pre-execution validation
function validateCode(code: string): boolean {
// Disallow fs, process, require
const forbidden = ['require', 'process', 'fs', 'child_process'];
for (const keyword of forbidden) {
if (code.includes(keyword)) return false;
}
return true;
}Memory System Design
Three-Layer Memory Architecture
┌─────────────────────────────────────────┐
│ Working Memory │
│ Current task context, part of LLM input │
│ • Recent conversation (compressed) │
│ • Current task state │
│ • Temporary variables │
├─────────────────────────────────────────┤
│ Episodic Memory │
│ Specific event records, persisted │
│ • Game event logs │
│ • Conversation history │
│ • Task completion records │
├─────────────────────────────────────────┤
│ Semantic Memory │
│ Abstract knowledge, vector retrieval │
│ • Game knowledge (recipes, item props) │
│ • Learned patterns │
│ • Player preferences │
└─────────────────────────────────────────┘
sql.js Local Storage
Why sql.js instead of SQLite native binding?
- No External Dependencies: Pure JavaScript implementation, easy to package
- Cross-platform Consistency: No dependency on system SQLite version
- Memory + Persistence: Supports pure memory operation and file persistence
import { Database } from 'sql.js';
// Initialize database
const db = new Database();
// Create table
db.run(`
CREATE TABLE IF NOT EXISTS episodic_memory (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp INTEGER NOT NULL,
event_type TEXT NOT NULL,
content TEXT NOT NULL,
embedding BLOB
)
`);
// Encrypted persistence
function saveToDisk(db: Database, password: string): void {
const data = db.export();
const encrypted = encrypt(data, password);
fs.writeFileSync('memory.db.enc', encrypted);
}Mineflayer Game Interaction
Event Listening Mechanism
// Listen to game events
bot.on('chat', (username, message) => {
if (username === bot.username) return;
// Trigger Agent response
agent.handleChatMessage(username, message);
});
bot.on('spawn', () => {
agent.handleSpawn();
});
bot.on('health', () => {
agent.handleHealthChange(bot.health, bot.food);
});
bot.on('death', () => {
agent.handleDeath();
});Operation Encapsulation
Wrap Mineflayer operations as Tools for LangChain Agent:
const tools = [
new Tool({
name: 'move_to',
description: 'Move to specified position',
parameters: {
type: 'object',
properties: {
x: { type: 'number' },
y: { type: 'number' },
z: { type: 'number' }
}
},
execute: async ({ x, y, z }) => {
await bot.pathfinder.goto(new GoalBlock(x, y, z));
return { success: true, position: bot.position };
}
}),
new Tool({
name: 'dig_block',
description: 'Dig specified block',
parameters: {
type: 'object',
properties: {
position: { type: 'object' }
}
},
execute: async ({ position }) => {
const block = bot.blockAt(position);
await bot.dig(block);
return { success: true, blockType: block.name };
}
})
];Comparison with Other ECHO Paths
| Feature | ECHO_feeling | ECHO_loop | ECHO_CLAW |
|---|---|---|---|
| Core Approach | Cognitive Science Architecture | React Agent Engineering | Context + Skill Invocation |
| Decision Source | Intrinsic Emergence | LLM Reasoning + Rules | Context-Driven |
| Knowledge Management | Cognitive Layer Management | Memory System | Memory Query |
| Emotional System | PAD/OCC Dual Engine | None (extensible) | None |
| Action Method | Intention Strength Driven | Tool-Calling | Skill Orchestration |
| Best For | Deep Immersive Experience | Engineering Practice | Universal Game Framework |
Development Experience
Electron Packaging Pitfalls
-
sql.js Loading Issue: sql.js needs to load wasm file, must handle correctly during packaging
// In webpack config new CopyWebpackPlugin({ patterns: [{ from: 'node_modules/sql.js/dist/sql-wasm.wasm', to: 'sql-wasm.wasm' }] }) -
Mineflayer Connection Issue: Network permissions differ between dev and production mode
LangChain Streaming Output
Streaming output is critical for user experience:
// Use LangChain's streaming callback
const agent = new ToolCallingAgent({
llm,
tools,
stream: true
});
for await (const chunk of agent.stream(input)) {
// Real-time send to frontend
ipcRenderer.send('agent-stream', chunk);
}Current Status & Outlook
Completed
- ✅ Electron + Vue 3 framework setup
- ✅ LangChain Agent basic implementation
- ✅ Mineflayer connection and operation encapsulation
- ✅ sql.js local memory storage
- ✅ Code Agent mode and sandbox
In Progress
- 🔄 Memory compression algorithm optimization
- 🔄 Long-term memory vector retrieval
- 🔄 Action log visualization
Future Plans
- 🔜 Multi-Agent collaboration (multiple bots running simultaneously)
- 🔜 Learning module (auto-learn from game Wiki)
- 🔜 Architecture comparison experiments with ECHO_feeling/ECHO_CLAW
Related Links
- GitHub Repository(opens in a new tab)
- ECHO_feeling Introduction
- ECHO_CLAW Design
- LangChain Documentation(opens in a new tab)
- Mineflayer Documentation(opens in a new tab)
Last updated: 2026-05-13