Files
val-blog/skills/vision/SKILL.md
T

139 lines
5.0 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.
---
name: "vision"
description: "Agent 识图工具 - Gemini 2.5 Flash Lite(首选)或本地 Qwen2.5-VL(降级)分析图像内容"
metadata:
author: Val
version: 2.3.0
tags: [vision, image, multimodal, gemini, local-model]
trigger: automatic
trigger_priority: high
---
# Vision 识图技能
## 目标
当 agent 收到图片消息时,自动调用视觉模型识别图片内容,并将描述纳入对话上下文,让 agent 能够"看见"图片。
## 触发条件(自动)
agent 在以下任一情况**必须自动触发**本技能,优先于内置 `image` 工具:
- 用户通过任何渠道(Telegram、微信、QQ 等)发送的图片消息
- 消息中包含图片附件(`<qqmedia>` 标签、`image_url`、文件附件等)
- 用户直接要求 agent 看图
- agent 需要理解图片内容才能继续对话
**自动执行流程:**
1. 检测到消息中有图片 → 优先使用本技能(走 Gemini curl 流程)
2. 不要依赖内置 `image` 工具,除非 Gemini curl 流程失败
3. 识图结果作为辅助信息写入对话上下文,让 agent 能基于图片内容回复用户
## 引擎策略
按优先级顺序:
1. **Gemini 2.5 Flash Lite**(首选)— 快(1-3 秒),准确,可用性高,通过环境变量 `GEMINI_API_KEY` 调用
2. **Gemini 2.5 Flash**(二选)— 更高精度,但免费层有时 503,仅在 Lite 失败时降级尝试
3. **本地 Qwen2.5-VL**(最终降级)— 纯本地,但 CPU 推理非常慢(10-90 秒),仅在前两者都不可用时回退
## 调用方式
### 第一步:定位图片文件
图片文件存放在 `/Users/guchen/.openclaw/media/inbound/` 目录下,文件名通过消息中的 UUID 标识获取。
例如消息中的 media 路径或 filename 中包含 UUID,对应的文件路径为 `/Users/guchen/.openclaw/media/inbound/{UUID}.jpg`
如果图片以 URL 形式传入,先下载到临时文件:
```bash
curl -sL -x http://127.0.0.1:7897 -o /tmp/vision_input.jpg "{图片URL}"
```
### 第二步:Gemini API(首选,通过 exec + curl
```bash
# 加载 GEMINI_API_KEY(注意:~/.zshrc 里 export 的值带双引号,
# 用 eval 展开确保去掉外层引号,否则 API 调用会报 key invalid
eval "$(grep '^export GEMINI_API_KEY=' ~/.zshrc 2>/dev/null)"
export HTTPS_PROXY=http://127.0.0.1:7897
VISION_B64=$(base64 -i /tmp/vision_input.jpg | tr -d '\n')
# 或者从 inbound 目录读取
VISION_B64=$(base64 -i /Users/guchen/.openclaw/media/inbound/{UUID}.jpg | tr -d '\n')
# 用 python3 构建 JSON 避免 shell 转义问题
curl -s --max-time 30 -x http://127.0.0.1:7897 \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent?key=***" \
-H "Content-Type: application/json" \
-d "$(python3 -c "
import json
b64 = '''$VISION_B64'''
payload = {
'contents': [{
'parts': [
{'text': '请用中文详细描述这张图片的内容'},
{'inline_data': {'mime_type': 'image/jpeg', 'data': b64}}
]
}]
}
print(json.dumps(payload))
")"
```
从响应中提取:`.candidates[0].content.parts[0].text`
如果 gemini-2.5-flash-lite 失败(429/503),尝试降级到 gemini-2.5-flash
- 将 URL 中的 `gemini-2.5-flash-lite` 替换为 `gemini-2.5-flash`
### 第三步:降级到本地 Qwen API
如果 Gemini 调用失败(超时、API key 未设置、网络不通、额度用完等),降级到本地模型:
```bash
VISION_B64=$(base64 -i /tmp/vision_input.jpg | tr -d '\n')
curl -s --max-time 120 http://127.0.0.1:8081/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "guff",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "请用中文详细描述这张图片的内容"},
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,"'$VISION_B64'"}}
]
}
],
"max_tokens": 500
}' 2>&1
```
从响应中提取:`.choices[0].message.content`
### 第四步:纳入上下文
将识图得到的文字描述以辅助信息形式写入对话上下文,格式参考:
```
[Vision: 图片描述内容...]
```
然后根据图片内容回复用户。
## 速度参考
| 引擎 | 延迟 | 备注 |
|---|---|---|
| Gemini 2.5 Flash Lite | 1-3 秒 | 首选,可用性高 |
| Gemini 2.5 Flash | 1-3 秒 | 二选,精度更高但有时 503 |
| 本地 Qwen2.5-VL | 10-90 秒 | CPU 推理,无网络依赖 |
## 注意事项
- 每次识图调用串行处理,不要并发多张图片
- 图片大小建议不超过 5MB,过大的图片应先压缩
- 识别结果不一定 100% 准确,在回复中适当表达不确定性
- 如果 Gemini 和本地模型都不可用,给出友好提示而非报错
- API key 存放于 `~/.zshrc` 中的 `GEMINI_API_KEY` 环境变量
- **密钥加载必须用 `eval "$(grep '^export GEMINI_API_KEY=' ~/.zshrc)"` 而不是 `source ~/.zshrc`,因为后者引入的变量值可能因 shell 引号上下文不一致而出错**
- 代理地址:`127.0.0.1:7897`