Skip to content

Commit 2d4a8c9

Browse files
committed
refactor(minecraft): 重构 SimpleAgent 类以优化环境感知和决策流程
- 移除冗余的环境分析代码,整合 StateAnalyzer 功能 - 优化上下文构建过程,提高代码可读性和效率 - 调整系统提示内容,简化并明确决策指南 - 修复代码中的一些小问题,提高整体代码质量
1 parent df100ad commit 2d4a8c9

1 file changed

Lines changed: 80 additions & 212 deletions

File tree

src/plugins/minecraft/agents/simple_agent.py

Lines changed: 80 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from mineland import Action
99

1010
from .base_agent import BaseAgent
11+
from src.plugins.minecraft.state.analyzers import StateAnalyzer
1112

1213

1314
class SimpleAgent(BaseAgent):
@@ -23,33 +24,7 @@ def __init__(self):
2324
self.max_memory_size: int = 10
2425
self.logger = logging.getLogger(__name__)
2526
self._is_initialized = False
26-
27-
# API验证集合
28-
self.valid_bot_methods = {
29-
"chat",
30-
"look",
31-
"lookAt",
32-
"dig",
33-
"placeBlock",
34-
"activateBlock",
35-
"setControlState",
36-
"clearControlStates",
37-
"equip",
38-
"unequip",
39-
"toss",
40-
"tossStack",
41-
"consume",
42-
"activateItem",
43-
"attack",
44-
"mount",
45-
"dismount",
46-
"setQuickBarSlot",
47-
"waitForTicks",
48-
"craft",
49-
"sleep",
50-
"wake",
51-
"respawn",
52-
}
27+
self.state_analyzer: Optional[StateAnalyzer] = None
5328

5429
async def initialize(self, config: Dict[str, Any]) -> None:
5530
"""初始化简单智能体"""
@@ -105,6 +80,13 @@ async def run(
10580
raise RuntimeError("简单智能体未初始化")
10681

10782
try:
83+
# 初始化或更新状态分析器
84+
if obs:
85+
if self.state_analyzer is None:
86+
self.state_analyzer = StateAnalyzer(obs=obs, config=self.config)
87+
else:
88+
self.state_analyzer.set_observation(obs)
89+
10890
# 构建上下文
10991
context = self._build_context(obs, code_info, done, task_info, maicore_command)
11092

@@ -125,7 +107,7 @@ async def run(
125107

126108
except Exception as e:
127109
self.logger.error(f"简单智能体执行错误: {e}")
128-
return Action(type=Action.NEW, code="// 等待下一步")
110+
return Action(type=Action.RESUME, code="")
129111

130112
def _validate_and_fix_action(self, action_code: str) -> str:
131113
"""验证并修复动作代码"""
@@ -158,99 +140,38 @@ def _build_context(
158140
task_info: Optional[Dict],
159141
maicore_command: Optional[str],
160142
) -> str:
161-
"""构建决策上下文 - 集成状态分析器的环境感知"""
143+
"""构建决策上下文 - 使用状态分析器获取环境感知"""
162144
context_parts = []
163145

164146
# === 环境感知部分 (优先级最高) ===
165-
if obs:
166-
# 使用状态分析器提供的环境分析
167-
if "environment_analysis" in obs:
168-
env_analysis = obs["environment_analysis"]
169-
if "summary" in env_analysis and env_analysis["summary"]:
170-
context_parts.append("=== 环境分析 ===")
171-
context_parts.extend(env_analysis["summary"])
172-
context_parts.append("") # 空行分隔
173-
174-
# 详细的环境信息
175-
if "detailed_environment" in obs:
176-
detailed_env = obs["detailed_environment"]
177-
178-
# 生命状态
179-
if "life_stats" in detailed_env and detailed_env["life_stats"]:
180-
context_parts.append("=== 生命状态 ===")
181-
context_parts.extend(detailed_env["life_stats"])
182-
context_parts.append("")
183-
184-
# 周围方块环境
185-
if "environment" in detailed_env and detailed_env["environment"]:
186-
context_parts.append("=== 周围环境 ===")
187-
context_parts.extend(detailed_env["environment"])
188-
context_parts.append("")
189-
190-
# 位置和方向
191-
if "position" in detailed_env and detailed_env["position"]:
192-
context_parts.append("=== 位置信息 ===")
193-
context_parts.extend(detailed_env["position"])
194-
if "direction" in detailed_env and detailed_env["direction"]:
195-
context_parts.extend(detailed_env["direction"])
196-
context_parts.append("")
197-
198-
# 移动障碍
199-
if "collision" in detailed_env and detailed_env["collision"]:
200-
context_parts.append("=== 移动状况 ===")
201-
context_parts.extend(detailed_env["collision"])
202-
if "facing_wall" in detailed_env and detailed_env["facing_wall"]:
203-
context_parts.extend(detailed_env["facing_wall"])
204-
context_parts.append("")
205-
206-
# 装备和物品
207-
if "equipment" in detailed_env and detailed_env["equipment"]:
208-
context_parts.append("=== 装备状态 ===")
209-
context_parts.extend(detailed_env["equipment"])
210-
context_parts.append("")
211-
212-
if "inventory" in detailed_env and detailed_env["inventory"]:
213-
context_parts.append("=== 物品栏 ===")
214-
context_parts.extend(detailed_env["inventory"])
215-
context_parts.append("")
216-
217-
# 时间和天气
218-
if "time" in detailed_env and detailed_env["time"]:
219-
context_parts.append("=== 游戏时间 ===")
220-
context_parts.extend(detailed_env["time"])
221-
if "weather" in detailed_env and detailed_env["weather"]:
222-
context_parts.extend(detailed_env["weather"])
223-
context_parts.append("")
224-
225-
# 如果没有状态分析器数据,使用原始观察数据
226-
if not any(key in obs for key in ["environment_analysis", "detailed_environment"]):
227-
# 基础状态信息
228-
health = obs.get("health", "未知")
229-
food = obs.get("food", "未知")
230-
position = obs.get("position", "未知")
231-
if health != "未知" or food != "未知" or position != "未知":
232-
context_parts.append(f"=== 基础状态 ===")
233-
context_parts.append(f"生命值: {health}, 饥饿值: {food}, 位置: {position}")
234-
context_parts.append("")
147+
if obs and self.state_analyzer:
148+
# 使用状态分析器提供的详细环境分析
149+
analysis_result = self.state_analyzer.analyze_all()
150+
if isinstance(analysis_result, list):
151+
context_parts.extend(analysis_result)
152+
else:
153+
context_parts.append(str(analysis_result))
235154

236155
# === 代码执行状态 ===
237156
if code_info:
238157
if code_info.get("code_error"):
239158
error_info = code_info["code_error"]
240-
context_parts.append("=== 执行错误 ===")
241-
context_parts.append(f"上次代码执行失败: {error_info.get('error_message', '未知错误')}")
242-
context_parts.append("")
159+
context_parts.extend(
160+
(
161+
"=== 执行错误 ===",
162+
f"上次代码执行失败: {error_info.get('error_message', '未知错误')}",
163+
"",
164+
)
165+
)
243166
elif code_info.get("is_ready"):
244-
context_parts.append("=== 执行状态 ===")
245-
context_parts.append("代码执行完成,准备下一个动作")
246-
context_parts.append("")
247-
167+
context_parts.extend(("=== 执行状态 ===", "代码执行完成,准备下一个动作", ""))
248168
# === 历史记忆 ===
249169
if self.memory:
250170
recent_memory = self.memory[-2:] # 最近2次记忆
251171
context_parts.append("=== 最近动作 ===")
252-
for i, memory in enumerate(recent_memory, 1):
253-
context_parts.append(f"{i}. {memory.get('action', '未知动作')}")
172+
context_parts.extend(
173+
f"{i}. {memory.get('action', '未知动作')}" for i, memory in enumerate(recent_memory, 1)
174+
)
254175
context_parts.append("")
255176

256177
# === MaiCore指令 (优先级最高) ===
@@ -273,16 +194,10 @@ def _build_context(
273194

274195
# === 任务信息 ===
275196
if task_info:
276-
context_parts.append("=== 任务信息 ===")
277-
context_parts.append(f"任务: {task_info}")
278-
context_parts.append("")
279-
197+
context_parts.extend(("=== 任务信息 ===", f"任务: {task_info}", ""))
280198
# 当前目标
281199
if self.current_goal:
282-
context_parts.append(f"=== 当前目标 ===")
283-
context_parts.append(f"目标: {self.current_goal}")
284-
context_parts.append("")
285-
200+
context_parts.extend(("=== 当前目标 ===", f"目标: {self.current_goal}", ""))
286201
return "\n".join(context_parts)
287202

288203
def _parse_maicore_command(self, command: str) -> Optional[str]:
@@ -307,18 +222,13 @@ def _parse_maicore_command(self, command: str) -> Optional[str]:
307222
self.logger.info(f"从MaiCore指令中提取到动作: {actions}")
308223
return actions.strip()
309224
except json.JSONDecodeError:
310-
# 如果不是JSON格式,尝试用正则表达式提取actions字段
311-
actions_match = re.search(r'"actions":\s*"([^"]+)"', command_clean)
312-
if actions_match:
225+
if actions_match := re.search(r'"actions":\s*"([^"]+)"', command_clean):
313226
actions = actions_match.group(1)
314227
self.logger.info(f"通过正则表达式提取到动作: {actions}")
315228
return actions
316229

317-
# 尝试提取不带引号的actions
318-
actions_match = re.search(r'"actions":\s*([^,}]+)', command_clean)
319-
if actions_match:
320-
actions = actions_match.group(1).strip().strip('"')
321-
if actions:
230+
if actions_match := re.search(r'"actions":\s*([^,}]+)', command_clean):
231+
if actions := actions_match.group(1).strip().strip('"'):
322232
self.logger.info(f"通过正则表达式提取到动作(无引号): {actions}")
323233
return actions
324234

@@ -340,10 +250,7 @@ async def _llm_decision(self, context: str) -> str:
340250
messages = [SystemMessage(content=self._get_system_prompt()), HumanMessage(content=context)]
341251

342252
response = await self.llm.ainvoke(messages)
343-
action_code = self._parse_action_from_response(str(response.content))
344-
345-
return action_code
346-
253+
return self._parse_action_from_response(str(response.content))
347254
except Exception as e:
348255
self.logger.error(f"LLM决策错误: {e}")
349256
return "// 等待"
@@ -386,92 +293,53 @@ def _rule_based_decision(self, obs: Dict, maicore_command: Optional[str]) -> str
386293

387294
def _get_system_prompt(self) -> str:
388295
"""获取系统提示 - 增强环境感知能力"""
389-
return """你是一个Minecraft智能体,能够感知和分析周围环境。根据详细的环境分析信息和当前状态,生成合适的动作代码。
390-
391-
## 环境感知能力
392-
你会收到以下类型的环境分析信息:
393-
- **环境分析**: 周围方块的分布、开阔程度、墙壁位置等
394-
- **生命状态**: 生命值、饥饿值、氧气值等
395-
- **位置信息**: 当前坐标、朝向、移动速度等
396-
- **移动状况**: 是否碰撞、前方是否有墙等
397-
- **装备状态**: 当前装备的工具和防具
398-
- **物品栏**: 拥有的物品和数量
399-
- **游戏时间**: 白天/夜晚、天气状况
400-
401-
## 动作指南
402-
根据环境分析信息做出智能决策:
403-
404-
### 移动决策
405-
- 如果"前方有墙",考虑挖掘或转向
406-
- 如果"处于开阔区域",可以自由移动探索
407-
- 如果"处于封闭空间",注意寻找出口
408-
409-
### 挖掘决策
410-
- 根据"周围方块"信息选择挖掘目标
411-
- 如果看到有价值的矿物,优先挖掘
412-
- 如果被困,挖掘脚下或上方的方块
413-
414-
### 生存决策
415-
- 根据"生命状态"调整行动策略
416-
- 生命值低时寻找食物或避开危险
417-
- 夜晚时寻找安全的地方
418-
419-
## 正确的API使用
420-
使用以下正确的mineflayer API:
421-
422-
### 移动控制
423-
```javascript
424-
// 移动
425-
bot.setControlState("forward", true) // 前进
426-
bot.setControlState("back", true) // 后退
427-
bot.setControlState("left", true) // 左移
428-
bot.setControlState("right", true) // 右移
429-
bot.setControlState("jump", true) // 跳跃
430-
bot.clearControlStates() // 停止所有移动
431-
432-
// 转向
433-
bot.look(bot.entity.yaw + Math.PI/2, bot.entity.pitch) // 右转90度
434-
bot.look(bot.entity.yaw - Math.PI/2, bot.entity.pitch) // 左转90度
435-
bot.look(bot.entity.yaw + Math.PI, bot.entity.pitch) // 转身180度
436-
```
437-
438-
### 方块操作
439-
```javascript
440-
// 挖掘
441-
bot.dig(bot.blockAt(bot.entity.position.offset(0, -1, 0))) // 挖脚下
442-
bot.dig(bot.blockAt(bot.entity.position.offset(0, 1, 0))) // 挖上方
443-
bot.dig(bot.blockAt(bot.entity.position.offset(0, 0, 1))) // 挖前方
444-
445-
// 放置方块
446-
bot.placeBlock(bot.blockAt(bot.entity.position.offset(0, -1, 0)), new Vec3(0, 1, 0))
447-
```
448-
449-
### 物品操作
450-
```javascript
451-
// 装备物品
452-
bot.equip(bot.inventory.slots[36], 'hand') // 装备到手中
453-
bot.unequip('hand') // 卸下手中物品
454-
455-
// 使用物品
456-
bot.activateItem(false) // 右键使用物品
457-
bot.consume() // 消耗物品(如食物)
458-
```
459-
460-
## 决策流程
461-
1. **分析环境**: 仔细阅读环境分析信息
462-
2. **评估状态**: 检查生命值、物品等状态
463-
3. **制定策略**: 根据环境和状态确定行动方案
464-
4. **选择动作**: 生成相应的API调用代码
465-
5. **确保语法**: 使用正确的JavaScript语法
296+
return """你是一个Minecraft智能体,任务是根据环境信息生成单行或多行的JavaScript动作代码。
297+
298+
## 决策指南
299+
- **分析环境**: 你会收到关于生命、位置、周围方块、物品栏等的详细信息。
300+
- **生存**: 当生命值或饥饿值低时,优先寻找食物或避开危险。夜晚时寻找安全之处。
301+
- **移动**:
302+
- 前方有墙时,考虑转向或挖掘。
303+
- 在开阔地带可以自由探索。
304+
- 在水里时,使用 `swimToLand` 游到岸边。
305+
- **挖掘**:
306+
- 根据周围方块信息,挖掘有价值的矿物(如 `diamond_ore`)或必要的材料(如 `oak_log`)。
307+
- 被困时,尝试挖掘出路。
308+
309+
## API参考
310+
**重要**: 只能使用下面列出的API。确保API名称和参数正确。
311+
312+
### 基础
313+
- `bot.chat("消息")`: 发送聊天消息 (请使用中文)。
314+
- `// 注释`: 用于写注释或表示"等待"。
315+
316+
### 移动
317+
- `bot.setControlState("状态", true/false)`: 设置移动状态。状态可以是 `forward`, `back`, `left`, `right`, `jump`, `sprint`。
318+
- `bot.clearControlStates()`: 停止所有移动。
319+
- `bot.look(yaw, pitch)`: 转向。示例:
320+
- `bot.look(bot.entity.yaw + Math.PI/2, bot.entity.pitch)`: 右转90度。
321+
- `bot.look(bot.entity.yaw - Math.PI/2, bot.entity.pitch)`: 左转90度。
322+
- `followPlayer(bot, "玩家名")`: 跟随玩家。
323+
- `swimToLand(bot)`: 游到最近的岸边。
324+
325+
### 方块
326+
- `mineBlock(bot, "方块名", 数量)`: 挖掘并收集指定数量的方块。
327+
- **注意**: 方块名必须正确,例如挖铁矿石是 `iron_ore`,而不是 `raw_iron`。
328+
- `placeItem(bot, "物品名", position)`: 在指定位置放置方块。
329+
330+
### 物品
331+
- `bot.equip(item, "位置")`: 装备物品到指定位置(如 'hand', 'head')。需要先在物品栏找到物品对象(item)。
332+
- `bot.unequip("位置")`: 卸下指定位置的装备。
333+
- `bot.activateItem(offhand)`: 使用手上的物品(`offhand`=false表示主手)。
334+
- `bot.consume()`: 消耗物品(例如吃食物)。
466335
467336
## 输出格式
468-
直接返回JavaScript代码,可以多行,例如:
469-
- `bot.setControlState("forward", true)`
470-
- `bot.dig(bot.blockAt(bot.entity.position.offset(0, -1, 0)))`
471-
- `bot.look(bot.entity.yaw + Math.PI/2, bot.entity.pitch)`
472-
- `// 等待观察环境`
473-
474-
不要包含解释或其他文本,只返回代码。"""
337+
- **只返回代码**: 不要包含任何解释、markdown标记(```)或多余的文本。
338+
- **代码示例**:
339+
- `bot.setControlState("forward", true);`
340+
- `mineBlock(bot, "oak_log", 1);`
341+
- `// 正在观察...`
342+
"""
475343

476344
def _parse_action_from_response(self, response: str) -> str:
477345
"""从LLM响应中解析动作代码"""

0 commit comments

Comments
 (0)