Files
val-blog/ai-agent-living/docs/TECH_SPEC.md
T

205 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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场景搭建