Add: 2026-07-20-云鲸邮局的最后一封信

This commit is contained in:
Chen Gu
2026-08-13 17:00:24 +08:00
parent b8ba9df1a1
commit 6cb843144b
218 changed files with 63602 additions and 35 deletions
+138
View File
@@ -0,0 +1,138 @@
---
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`