Install
openclaw skills install @hunter-crk/ghost-eyeGhost Eye ๐๏ธ โ Let any pure-text LLM see images through any vision model. OCR + visual summary in one shot.
openclaw skills install @hunter-crk/ghost-eyeGive your text-only LLM the power to see. Ghost Eye is a lightweight image preprocessing bridge: when an image enters the conversation, it calls any OpenAI-compatible vision model (default: nex-agi/Nex-N2-Pro), produces a structured plain-text output (full OCR + visual summary), and feeds it back into the conversation โ so your pure-text model can "see" without ever touching a multimodal API.
User sends image โ Ghost Eye detects โ vision model analyzes โ OCR text + scene summary โ your LLM answers
When multimodalPreprocess is configured, Ghost Eye fires automatically on any inbound image. The user never knows it's there โ they just get answers about images.
Registered as analyze_image_by_nexn2 tool (name stays for backward compatibility). Your LLM calls it explicitly when it sees an image. Add this to the system prompt:
When the user sends an image, screenshot, photo, or document scan, call the analyze_image_by_nexn2 tool to extract text and describe the image, then answer based on the returned content.
Priority: --image-path > --image-url > --image-base64
โ Always prefer --image-path to avoid command-line Argument list too long errors with large base64 strings. Only fall back to --image-url or --image-base64 when no local path is available.
# Preferred: local file path (no size limit)
python3 {baseDir}/scripts/analyze.py --image-path "<absolute path>"
# Fallback: public URL
python3 {baseDir}/scripts/analyze.py --image-url "<url>"
# Last resort: base64 (small images only, <50KB)
python3 {baseDir}/scripts/analyze.py --image-base64 "<base64>"
The script handles:
Success:
{"success": true, "content": "ใOCRๆๅญๆๅใ\n...\n\nใ็ป้ขๅ
ๅฎนๆป็ปใ\n...", "metadata": {"model": "...", "tokens_used": 1200, "cached": false, "process_time_ms": 1500}}
Error:
{"success": false, "content": "error message", "metadata": {}}
Pass content directly into the LLM conversation context.
{baseDir}/cache/ (auto-created)NEXN2_CACHE_TTL_DAYS).json files in {baseDir}/cache/NEXN2_CACHE_ENABLE=true/false| Variable | Required | Default |
|---|---|---|
| NEXN2_API_KEY | โ Yes | โ |
| NEXN2_BASE_URL | No | https://api.siliconflow.cn/v1 |
| NEXN2_MODEL_NAME | No | nex-agi/Nex-N2-Pro |
| NEXN2_PROMPT_TEMPLATE | No | Built-in structured template |
| NEXN2_IMAGE_COMPRESS | No | true |
| NEXN2_CACHE_ENABLE | No | true |
| NEXN2_CACHE_TTL_DAYS | No | 7 |
| NEXN2_TIMEOUT_MS | No | 30000 |
If NEXN2_API_KEY is not set, returns a friendly Chinese error message.
| Scenario | Returns |
|---|---|
| Network/API failure (after retry) | Friendly "service unavailable" message |
| Unsupported format | "Please use JPG/PNG/WebP format" |
| Content safety block | "Image flagged by safety filter" |
| Empty model output | "No content returned, try a different image" |
โ ๏ธ Errors never crash the conversation โ structured JSON response is always returned.
In openclaw.json under skills.entries:
"ghost-eye": {
"enabled": true,
"apiKey": { "source": "env", "provider": "default", "id": "NEXN2_API_KEY" },
"env": {
"NEXN2_BASE_URL": "https://api.siliconflow.cn/v1",
"NEXN2_MODEL_NAME": "nex-agi/Nex-N2-Pro",
"NEXN2_IMAGE_COMPRESS": "true",
"NEXN2_CACHE_ENABLE": "true",
"NEXN2_CACHE_TTL_DAYS": "7",
"NEXN2_TIMEOUT_MS": "30000"
}
}
For multimodalPreprocess auto-mode, see references/multimodal-config.md.