# Exploration Agent 使用指南

Exploration Agent 是基于 AI 视觉语言模型的手机自动化探索测试框架，能够理解屏幕内容并自动执行操作完成探索测试任务。支持 Android、iOS 和 HarmonyOS 三端。

## 快速开始

### 1. 配置和初始化

```python
from ubox_py_sdk import UBox
from ubox_py_sdk.models import OSType
from ubox_py_sdk.phone_agent.model import ModelConfig
from ubox_py_sdk.phone_agent import AgentConfig

# 配置 AI 模型
agent_model_config = ModelConfig(
    base_url="http://localhost:8000/v1",  # 模型服务地址
    api_key="EMPTY",  # API密钥（如果不需要认证可设为"EMPTY"）
    model_name="autoglm-phone-9b",  # 模型名称
)

# 配置 Agent（可选）
agent_config = AgentConfig(
    max_steps=100,  # 最大执行步数
    verbose=True,  # 是否显示详细日志
)

# 创建 UBox 客户端
ubox = UBox(
    secret_id="your_secret_id",
    secret_key="your_secret_key",
    agent_model_config=agent_model_config,  # 模型配置
    agent_config=agent_config,  # 可选：Agent配置
)

# 初始化设备
device = ubox.init_device(
    udid="device-001",
    os_type=OSType.ANDROID,
)
```

### 2. 使用方式

`device.explore()` 返回一个生成器，必须使用 `for` 循环来迭代获取执行状态：

```python
# 使用生成器实时获取执行状态
for update in device.explore("打开微信并发送消息给张三"):
    # update 是 StepUpdate 对象
    print(f"步骤 {update.step}, 类型: {update.update_type}")
    print(f"内容: {update.content}")
```

**如果只需要最终结果，可以这样处理：**

```python
final_message = None
for update in device.explore("打开微信并发送消息给张三"):
    if update.update_type == "finished":
        content = update.content
        final_message = content.get("message") if isinstance(content, dict) else content
        break

print(final_message)  # 输出：任务完成或最终消息
```

## StepUpdate 返回内容说明

Agent 通过流式接口返回 `StepUpdate` 对象，包含以下字段：

- `step`: 步骤编号（int）
- `update_type`: 更新类型（字符串，见下方说明）
- `content`: 更新内容（根据类型不同而不同）

### update_type 类型

Agent 会返回以下 5 种类型的更新：

1. **`"thinking"`** - AI 思考过程
2. **`"model_complete"`** - 模型处理完成（包含统计信息）
3. **`"action"`** - 要执行的动作信息
4. **`"result"`** - 动作执行结果
5. **`"finished"`** - 任务完成

## 各类型返回内容详解

### 1. thinking - AI 思考过程

**update_type**: `"thinking"`

**content**: `str` - AI 思考过程的文本片段

**示例：**

```python
for update in device.explore("打开微信"):
    if update.update_type == "thinking":
        print(update.content, end="", flush=True)  # 流式输出思考过程
```

**输出示例：**
```
我需要先查看当前屏幕，找到微信应用的图标...
我看到屏幕上有微信图标，位于屏幕底部...
我将点击微信图标来打开应用...
```

### 2. model_complete - 模型处理完成

**update_type**: `"model_complete"`

**content**: `dict` - 包含以下可选字段：

- `tokens`: `dict` - Token 统计信息（可选）
  - `prompt_tokens`: `int` - 输入 token 数
  - `completion_tokens`: `int` - 输出 token 数
  - `total_tokens`: `int` - 总 token 数
- `timing`: `dict` - 推理时长统计（可选）
  - `time_to_first_token`: `float` - 首 Token 延迟（秒）
  - `time_to_thinking_end`: `float` - 思考完成延迟（秒）
  - `total_time`: `float` - 总推理时间（秒）

**示例：**

```python
for update in device.explore("打开微信"):
    if update.update_type == "model_complete":
        content = update.content  # dict
        
        # Token 统计（如果有）
        tokens = content.get("tokens")
        if tokens:
            print(f"Token: 输入={tokens.get('prompt_tokens')}, "
                  f"输出={tokens.get('completion_tokens')}, "
                  f"总计={tokens.get('total_tokens')}")
        
        # 推理时长统计（如果有）
        timing = content.get("timing")
        if timing:
            print(f"推理时长: 首Token={timing.get('time_to_first_token'):.3f}s, "
                  f"总时长={timing.get('total_time'):.3f}s")
```

**注意：** 无论是否有统计信息，每轮推理完成后都会发送 `model_complete` 信号（`content` 可能为空字典 `{}`）。

### 3. action - 动作信息

**update_type**: `"action"`

**content**: `dict` - 包含以下字段：

- `action`: `dict` - 要执行的动作字典（ActionDict）
- `message`: `str` - 操作描述（简要说明本次操作）
- `screenshot_before`: `str` - 操作前的截图（base64，已标注操作位置）

**action 字典结构示例：**

```python
# 点击操作
{
    "_metadata": "do",
    "action": "Tap",
    "element": [500, 500],  # 相对坐标（0-1000，左上角(0,0)到右下角(999,999)）或绝对坐标（像素）
    "desc": "点击登录按钮"  # 操作描述（可选）
}

# 滑动操作
{
    "_metadata": "do",
    "action": "Swipe",
    "start": [500, 800],  # 起始坐标（相对坐标0-1000或绝对坐标）
    "end": [500, 200],    # 结束坐标（相对坐标0-1000或绝对坐标）
    "desc": "向上滑动查看内容"
}

# 输入文本
{
    "_metadata": "do",
    "action": "Type",
    "text": "Hello World",
    "desc": "输入搜索关键词"
}

# 启动应用
{
    "_metadata": "do",
    "action": "Launch",
    "app": "com.tencent.mm",  # 包名或应用名称
    "desc": "启动微信应用"
}

# 完成任务
{
    "_metadata": "finish",
    "message": "任务完成"
}
```

**示例：**

```python
for update in device.explore("打开微信"):
    if update.update_type == "action":
        content = update.content  # dict
        
        action = content.get("action")  # ActionDict
        message = content.get("message")  # str
        screenshot_before = content.get("screenshot_before")  # str (base64)
        
        print(f"操作: {message}")
        print(f"动作类型: {action.get('action')}")  # 如 "Tap", "Swipe", "Launch" 等
```

### 4. result - 执行结果

**update_type**: `"result"`

**content**: `dict` - 包含以下字段：

- `success`: `bool` - 执行是否成功
- `message`: `str` - 执行结果消息
- `screenshot_after`: `str` - 操作后的截图（base64）

**示例：**

```python
for update in device.explore("打开微信"):
    if update.update_type == "result":
        content = update.content  # dict
        
        success = content.get("success")  # bool
        message = content.get("message")  # str
        screenshot_after = content.get("screenshot_after")  # str (base64)
        
        if success:
            print(f"✅ 执行成功: {message}")
        else:
            print(f"❌ 执行失败: {message}")
```

### 5. finished - 任务完成

**update_type**: `"finished"`

**content**: `dict` - 包含以下字段：

- `success`: `bool` - 任务是否成功完成
- `message`: `str` - 完成消息

**示例：**

```python
for update in device.explore("打开微信"):
    if update.update_type == "finished":
        content = update.content  # dict
        
        success = content.get("success")  # bool
        message = content.get("message")  # str
        
        if success:
            print(f"🎉 任务完成: {message}")
        else:
            print(f"⚠️ 任务结束: {message}")
        
        # 收到 finished 后，生成器将结束，不会再产生新的更新
        break
```

## 完整使用示例

```python
from ubox_py_sdk import UBox
from ubox_py_sdk.models import OSType
from ubox_py_sdk.phone_agent.model import ModelConfig
from ubox_py_sdk.phone_agent import AgentConfig

# 配置
model_config = ModelConfig(
    base_url="http://localhost:8000/v1",
    api_key="EMPTY",
    model_name="autoglm-phone-9b",
)

ubox = UBox(
    secret_id="your_secret_id",
    secret_key="your_secret_key",
    agent_model_config=model_config,
)

# 初始化设备
device = ubox.init_device(
    udid="device-001",
    os_type=OSType.ANDROID,
)

# 执行任务
for update in device.explore("打开微信，找到家庭AI小助手，发送你好"):
    if update.update_type == "thinking":
        # 实时显示思考过程
        print(update.content, end="", flush=True)
    
    elif update.update_type == "model_complete":
        # 模型处理完成，显示统计信息
        content = update.content
        tokens = content.get("tokens")
        timing = content.get("timing")
        
        if tokens:
            print(f"\n[步骤 {update.step}] Token: {tokens.get('total_tokens')}")
        if timing:
            print(f"[步骤 {update.step}] 推理时长: {timing.get('total_time'):.3f}s")
    
    elif update.update_type == "action":
        # 显示要执行的操作
        message = update.content.get("message", "")
        print(f"\n[步骤 {update.step}] 操作: {message}")
    
    elif update.update_type == "result":
        # 显示执行结果
        success = update.content.get("success", False)
        message = update.content.get("message", "")
        status = "✅" if success else "❌"
        print(f"[步骤 {update.step}] {status} {message}")
    
    elif update.update_type == "finished":
        # 任务完成
        success = update.content.get("success", False)
        message = update.content.get("message", "")
        if success:
            print(f"\n🎉 任务完成: {message}")
        else:
            print(f"\n⚠️ 任务结束: {message}")
        break

# 关闭客户端
ubox.close()
```

## 配置参数说明

### ModelConfig

```python
ModelConfig(
    base_url="http://localhost:8000/v1",  # 模型服务的基础URL
    api_key="EMPTY",  # API密钥
    model_name="autoglm-phone-9b",  # 模型名称
    max_tokens=3000,  # 最大生成token数，默认3000
    temperature=0.0,  # 温度参数，默认0.0
    top_p=0.85,  # Top-p采样参数，默认0.85
    frequency_penalty=0.2,  # 频率惩罚，默认0.2
)
```

### AgentConfig

```python
AgentConfig(
    max_steps=100,  # 最大执行步数，默认100
    verbose=True,  # 是否显示详细日志，默认True
    system_prompt=None,  # 自定义系统提示词，默认使用内置提示词
)
```
