feat(val-blog): add 2026-04-30 dream journey post
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
# AgentLiving - 技术规格文档
|
||||
|
||||
## 1. Godot 项目结构
|
||||
|
||||
```
|
||||
ai-agent-living/
|
||||
├── assets/
|
||||
│ ├── sprites/
|
||||
│ │ ├── val/
|
||||
│ │ │ ├── idle.png
|
||||
│ │ │ ├── walk.png
|
||||
│ │ │ └── interact.png
|
||||
│ │ └── furniture/
|
||||
│ │ ├── sofa.png
|
||||
│ │ ├── fridge.png
|
||||
│ │ └── bed.png
|
||||
│ ├── tilesets/
|
||||
│ │ └── apartment_floor.tres
|
||||
│ └── audio/
|
||||
│ └── ambient.mp3
|
||||
├── scenes/
|
||||
│ ├── main.tscn
|
||||
│ ├── apartment.tscn
|
||||
│ ├── val.tscn
|
||||
│ └── furniture/
|
||||
│ ├── sofa.tscn
|
||||
│ └── fridge.tscn
|
||||
├── scripts/
|
||||
│ ├── val.gd
|
||||
│ ├── apartment.gd
|
||||
│ ├── bridge_client.gd
|
||||
│ └── state_manager.gd
|
||||
└── project.godot
|
||||
```
|
||||
|
||||
## 2. 关键类设计
|
||||
|
||||
### 2.1 Val (CharacterBody2D)
|
||||
```gdscript
|
||||
class_name Val
|
||||
extends CharacterBody2D
|
||||
|
||||
@export var speed: float = 100.0
|
||||
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
|
||||
@onready var thought_bubble: Label = $ThoughtBubble
|
||||
|
||||
var current_state: String = "idle"
|
||||
var target_position: Vector2 = Vector2.ZERO
|
||||
var agent_id: String = "val"
|
||||
|
||||
func _ready():
|
||||
BridgeClient.connect("action_received", _on_action)
|
||||
_connect_to_openclaw()
|
||||
|
||||
func _physics_process(delta):
|
||||
match current_state:
|
||||
"walk": _handle_walk(delta)
|
||||
"interact": _handle_interact(delta)
|
||||
"think": _handle_think(delta)
|
||||
_: _handle_idle(delta)
|
||||
|
||||
func move_to(pos: Vector2):
|
||||
target_position = pos
|
||||
current_state = "walk"
|
||||
sprite.play("walk")
|
||||
|
||||
func _on_action(action: Dictionary):
|
||||
match action.cmd:
|
||||
"walk_to": move_to(Vector2(action.pos[0], action.pos[1]))
|
||||
"play_anim": sprite.play(action.anim)
|
||||
"show_thought": _show_thought(action.text)
|
||||
"update_state": _update_internal_state(action.key, action.value)
|
||||
```
|
||||
|
||||
### 2.2 BridgeClient (WebSocket)
|
||||
```gdscript
|
||||
extends Node
|
||||
|
||||
signal action_received(action: Dictionary)
|
||||
signal state_synced(state: Dictionary)
|
||||
|
||||
var socket: WebSocketPeer
|
||||
var openclaw_url: String = "ws://127.0.0.1:18789"
|
||||
|
||||
func _ready():
|
||||
_connect()
|
||||
|
||||
func _connect():
|
||||
socket = WebSocketPeer.new()
|
||||
socket.connect_to_url(openclaw_url)
|
||||
|
||||
func _process(delta):
|
||||
socket.poll()
|
||||
var state = socket.get_ready_state()
|
||||
if state == WebSocketPeer.STATE_OPEN:
|
||||
while socket.get_available_packets() > 0:
|
||||
var packet = socket.get_packet()
|
||||
var message = JSON.parse_string(packet.get_string_from_utf8())
|
||||
_handle_message(message)
|
||||
|
||||
func send_command(command: Dictionary):
|
||||
if socket.get_ready_state() == WebSocketPeer.STATE_OPEN:
|
||||
socket.send_text(JSON.stringify(command))
|
||||
|
||||
func _handle_message(msg: Dictionary):
|
||||
match msg.type:
|
||||
"action_sequence":
|
||||
for action in msg.actions:
|
||||
action_received.emit(action)
|
||||
"state_sync":
|
||||
state_synced.emit(msg.state)
|
||||
```
|
||||
|
||||
## 3. OpenClaw端集成
|
||||
|
||||
### 3.1 AgentLiving Skill
|
||||
创建 `~/.openclaw/skills/agent-living/`:
|
||||
|
||||
```
|
||||
agent-living/
|
||||
├── SKILL.md
|
||||
├── scripts/
|
||||
│ └── bridge_server.py
|
||||
└── config.json
|
||||
```
|
||||
|
||||
### 3.2 Bridge Server (Python)
|
||||
```python
|
||||
# bridge_server.py
|
||||
import asyncio
|
||||
import websockets
|
||||
import json
|
||||
from openclaw import session_manager
|
||||
|
||||
class AgentLivingBridge:
|
||||
def __init__(self):
|
||||
self.agents = {}
|
||||
self.godot_clients = {}
|
||||
|
||||
async def handle_godot(self, websocket, path):
|
||||
"""处理Godot客户端连接"""
|
||||
async for message in websocket:
|
||||
data = json.loads(message)
|
||||
response = await self._process_command(data)
|
||||
await websocket.send(json.dumps(response))
|
||||
|
||||
async def _process_command(self, cmd: dict) -> dict:
|
||||
"""将Godot指令转换为Agent动作"""
|
||||
if cmd["type"] == "user_command":
|
||||
# 调用OpenClaw Agent处理
|
||||
agent_session = session_manager.get("val")
|
||||
result = await agent_session.send_message(
|
||||
f"用户在Godot中发出指令:{cmd}"
|
||||
)
|
||||
return self._parse_agent_response(result)
|
||||
|
||||
def _parse_agent_response(self, response: str) -> dict:
|
||||
"""解析Agent回复为Godot动作序列"""
|
||||
# 提取动作序列,格式化为Godot可执行的JSON
|
||||
actions = []
|
||||
# ... 解析逻辑
|
||||
return {"type": "action_sequence", "actions": actions}
|
||||
|
||||
# 启动服务器
|
||||
async def main():
|
||||
bridge = AgentLivingBridge()
|
||||
async with websockets.serve(
|
||||
bridge.handle_godot,
|
||||
"localhost",
|
||||
8765
|
||||
):
|
||||
await asyncio.Future() # 永久运行
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## 4. 状态同步策略
|
||||
|
||||
### 4.1 心跳机制
|
||||
- Godot每5秒发送状态查询
|
||||
- OpenClaw立即返回完整状态
|
||||
- 差异超过阈值时触发同步
|
||||
|
||||
### 4.2 事件驱动
|
||||
- Agent状态变化 → 立即推送到Godot
|
||||
- 用户交互 → 立即发送到Agent
|
||||
- 避免轮询开销
|
||||
|
||||
## 5. 性能优化
|
||||
|
||||
### 5.1 Agent端
|
||||
- 简单行为(走动、idle)用本地规则
|
||||
- 复杂决策(任务分解)才调用LLM
|
||||
- 缓存常用响应
|
||||
|
||||
### 5.2 Godot端
|
||||
- 对象池复用粒子效果
|
||||
- 远处角色降低动画帧率
|
||||
- 按需加载房间(视野外不渲染)
|
||||
|
||||
---
|
||||
|
||||
**下一步:** Week 1 开始Godot场景搭建
|
||||
Reference in New Issue
Block a user