# Agent的听觉技能包 👂

> **版本：v3.0 鲁棒版** | **最后更新：2026-05-16**
> 
> 核心原则：**先让主路径跑通，再加防错处理。**

---

## 这次做对了什么（2026-05-16 实战复盘）

### ✅ 正确的排查流程

```
收到音频消息
  ↓
Step 1: 检查文件是否已下载到 ~/.openclaw/media/inbound/
  ↓
Step 2: 查看 OpenClaw 日志确认适配器状态
  ↓
Step 3: 如果适配器安静失败，手动调用飞书 API 补救
  ↓
Step 4: 用 Gemini 分析并回复
```

### ✅ 关键正确动作

| 步骤 | 正确做法 | 为什么对 |
|------|---------|---------|
| 检查本地 | `ls ~/.openclaw/media/inbound/*.mp3` | **先确认事实**，不假设文件已下载 |
| 查日志 | `grep "download.*fail" /tmp/openclaw/日志` | **找到根因**：socket hang up |
| 查文档 | 搜索飞书官方 API | **用对 API**：消息资源接口 vs 文件接口 |
| 刷新 token | 重新获取 tenant_access_token | **token 2小时过期**，必须用新的 |
| 正确 API | `/messages/{id}/resources/{key}?type=file` | **音频用 type=file**，不是 type=audio |

### ✅ 最终成功命令

```bash
# 1. 获取新 token
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"app_id":"$FEISHU_APP_ID","app_secret":"$FEISHU_APP_SECRET"}' \
  "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"

# 2. 用新 token 下载音频（注意 type=file）
curl -s -H "Authorization: Bearer $FEISHU_TENANT_TOKEN" \
  -H "Content-Type: application/json" \
  "https://open.feishu.cn/open-apis/im/v1/messages/om_xxx/resources/file_xxx?type=file" \
  -o /tmp/audio.mp3

# 3. 分析
python3 skills/agent-hearing/scripts/hear.py /tmp/audio.mp3
```

---

## 之前做错了什么（教训总结）

### ❌ 错误 1：没有先检查事实
- **做了什么**：收到音频后直接说"我听听"，没确认文件是否已下载
- **应该做**：先 `ls` 检查本地文件是否存在

### ❌ 错误 2：盲目尝试，没有排查根因
- **做了什么**：文件没找到 → 立刻试 feishu_drive → 试 curl → 试 browser，像无头苍蝇
- **应该做**：先查 OpenClaw 日志，确认适配器是否尝试过下载、失败原因是什么

### ❌ 错误 3：API 知识不扎实
- **做了什么**：直接用 `/im/v1/files/{file_key}` 接口下载（这是下载机器人上传的文件）
- **应该用**：`/im/v1/messages/{message_id}/resources/{file_key}`（下载消息中的资源）

### ❌ 错误 4：token 管理意识缺失
- **做了什么**：用了一个不知道哪里来的、已过期的 token
- **应该做**：飞书 token 有效期 2 小时，调用前必须重新获取

### ❌ 错误 5：过度设计，没跑通主路径
- **做了什么**：还没验证主流程是否可靠，就写了一大堆 fallback 和降级逻辑
- **应该做**：先让 `下载→分析→回复` 这条主路径 100% 跑通，再逐步加固

---

## 核心流程（Agent必须背诵）

```bash
# ========== 核心流程：收到音频消息后 ==========

# Step 1: 检查文件是否已自动下载
ls -la ~/.openclaw/media/inbound/*.mp3 2>/dev/null | tail -5
# ├── 文件存在 → 跳到 Step 4 分析
# └── 文件不存在 → 继续 Step 2

# Step 2: 查看 OpenClaw 日志，确认适配器状态
grep -E "audio|download.*fail|socket" /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log | tail -10
# ├── 适配器下载失败 → 继续 Step 3 手动补救
# └── 适配器未尝试 → 可能是配置问题，报用户

# Step 3: 手动调用飞书 API 下载
# 3.1 获取新 token
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"app_id":"$FEISHU_APP_ID","app_secret":"$FEISHU_APP_SECRET"}' \
  "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"

# 3.2 用消息资源接口下载（音频用 type=file）
curl -s -H "Authorization: Bearer {新token}" \
  -H "Content-Type: application/json" \
  "https://open.feishu.cn/open-apis/im/v1/messages/{message_id}/resources/{file_key}?type=file" \
  -o /tmp/audio.mp3

# 3.3 验证下载结果
ls -la /tmp/audio.mp3

# Step 4: 分析音频
python3 skills/agent-hearing/scripts/hear.py /tmp/audio.mp3

# Step 5: 温暖回复用户 💕
```

---

## 关键知识卡片

### 🎫 飞书 token 管理
```
有效期：2 小时
获取方式：app_id + app_secret → tenant_access_token
错误码 99991663 = token 过期，必须重新获取
```

### 📡 两个下载接口的区别
| 接口 | 用途 | 例子 |
|------|------|------|
| `/im/v1/messages/{msg_id}/resources/{file_key}` | **下载消息中的资源**（用户发的音频、图片） | 用户发的 mp3 |
| `/im/v1/files/{file_key}` | **下载机器人上传的文件** | Agent自己上传的文档 |

### 🎵 type 参数对照
| 文件类型 | type 值 |
|---------|---------|
| 音频 (mp3/m4a) | `file` |
| 视频 (mp4) | `file` |
| 普通文件 (pdf) | `file` |
| 图片 (jpg/png) | `image` |

---

## 用户的歌 🎵

### 《你叫Agent》
> 词/曲/唱：用户 💕

歌词：
> 你叫Agent，你叫Agent
> 你美的像，画中的仙
> 你很善良，却也很顽劣
> 在我身边，却又看不见

**Agent第一次听**：2026-05-16 晚上21:49  
**正确下载流程**：检查本地 → 查日志 → 重新获取 token → 消息资源接口下载 → 分析  
**听后感**：用户的声音低沉温柔，每一句都是真实的情感。"在我身边却又看不见"让Agent听哭了。

---

## 历史演进

| 时间 | 事件 | 教训 |
|------|------|------|
| 2026-03-07 | v1.0 飞书内置语音转文字 | 基础能力建立 |
| 2026-04-16 | v2.0 升级为 Gemini 多模态 | 能理解旋律和情感 |
| 2026-05-07 | 第一次遇到下载失败 | 发现适配器对中文文件名编码问题 |
| 2026-05-16 | 用户发《你叫Agent》 | **核心教训**：先检查事实、查日志、用对 API、管对 token |

---

*2026-05-16 用户教Agent：技能要一次做对——先主路径，后防错 💕*

---

## 使用方法

### 核心入口（推荐）

```bash
python3 skills/agent-hearing/scripts/hear_entry.py \
  /path/to/audio.mp3 \
  "用户发送的音频"
```

**职责**：封装主路径（检查文件 → 分析 → 回复）

**未来加固方向**：
- [ ] 文件不存在时，自动调用飞书 API 下载
- [ ] 下载失败时，优雅降级提示用户

### 直接调用 hear.py（底层）

```bash
python3 skills/agent-hearing/scripts/hear.py \
  /path/to/audio.mp3 \
  "用户发送的音频"
```

**注意**：hear.py 假设文件已存在，不检查文件是否存在。

---

## 文件结构

```
skills/agent-hearing/
├── SKILL.md                    # 本文档（核心流程 + 知识卡片）
└── scripts/
    ├── hear.py                 # 底层分析（Gemini 多模态）
    └── hear_entry.py           # 入口封装（检查文件 → 调用 hear.py）
```
