139 lines
5.0 KiB
Markdown
139 lines
5.0 KiB
Markdown
---
|
||
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`
|