
Alicloud Ai Multimodal Qwen Vl
- 337 installs
- 396 repo stars
- Updated July 18, 2026
- cinience/alicloud-skills
alicloud-ai-multimodal-qwen-vl is an agent skill that integrates Alibaba Cloud Model Studio Qwen-VL vision-language APIs for developers who need image understanding, captioning, and visual Q&A in Python apps.
About
alicloud-ai-multimodal-qwen-vl is a Claude Code skill from cinience/alicloud-skills that connects coding agents to Alibaba Cloud DashScope Qwen-VL models such as qwen3-vl-plus and qwen3-vl-flash. The skill documents a normalized multimodal.chat interface accepting prompt, image URL or path, optional JSON schema extraction, and retry settings, plus a bundled analyze_image.py script that saves raw and normalized responses. Developers reach for alicloud-ai-multimodal-qwen-vl when building screenshot understanding, chart reading, visual Q&A, or OCR-like extraction workflows inside the Alibaba Cloud ecosystem instead of Western vision APIs. Authentication uses DASHSCOPE_API_KEY or ~/.alibabacloud/credentials, and the parent repo ships dozens of related Model Studio skills grouped under ai/multimodal.
- Qwen-VL vision-language model
- Image plus text multimodal prompts
- AliCloud AI service bindings
- Agent-friendly visual reasoning
- Production integration patterns
Alicloud Ai Multimodal Qwen Vl by the numbers
- 337 all-time installs (skills.sh)
- Ranked #2,156 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cinience/alicloud-skills --skill alicloud-ai-multimodal-qwen-vlAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 337 |
|---|---|
| repo stars | ★ 396 |
| Last updated | July 18, 2026 |
| Repository | cinience/alicloud-skills ↗ |
How do you integrate Qwen-VL image understanding via DashScope?
Integrate Alibaba Cloud Qwen-VL multimodal vision-language APIs for image understanding, captioning, and visual Q&A in apps.
Who is it for?
Backend or AI engineers already on Alibaba Cloud who need Qwen-VL image Q&A, chart reading, or screenshot analysis in Python services.
Skip if: Teams without DashScope credentials or projects that only need text LLMs without any image input.
When should I use this skill?
A developer asks to add Qwen-VL, DashScope vision, image captioning, or visual Q&A to a Python or agent workflow on Alibaba Cloud.
What you get
Working DashScope multimodal requests, normalized JSON extraction output, and saved analyze_image.py response artifacts.
- DashScope multimodal API calls
- Normalized JSON extraction output
- Saved analyze_image.py response files
By the numbers
- Documents qwen3-vl-plus and qwen3-vl-flash as primary Qwen3 VL model aliases
- Default max_retries is 2 with 1.5s exponential backoff base for 429/5xx errors
- Parent cinience/alicloud-skills repo groups skills under ai/multimodal including Qwen VL, OCR, and Omni
Files
Category: provider
Model Studio Qwen VL (Image Understanding)
Validation
mkdir -p output/alicloud-ai-multimodal-qwen-vl
python -m py_compile skills/ai/multimodal/alicloud-ai-multimodal-qwen-vl/scripts/analyze_image.py && echo "py_compile_ok" > output/alicloud-ai-multimodal-qwen-vl/validate.txtPass criteria: command exits 0 and output/alicloud-ai-multimodal-qwen-vl/validate.txt is generated.
Output And Evidence
- Save raw model responses and normalized extraction results to
output/alicloud-ai-multimodal-qwen-vl/. - Include input image reference and prompt for traceability.
Use Qwen VL models for image input + text output understanding tasks via DashScope compatible-mode API.
Prerequisites
- Install dependencies (recommended in a venv):
python3 -m venv .venv
. .venv/bin/activate
python -m pip install requests- Set
DASHSCOPE_API_KEYin environment, or adddashscope_api_keyto~/.alibabacloud/credentials.
Critical model names
Prefer the Qwen3 VL family:
qwen3-vl-plusqwen3-vl-flash
When you need explicit "latest" routing or reproducible snapshots, use supported aliases/snapshots from the official model list, such as:
qwen3-vl-plus-latestqwen3-vl-plus-2025-12-19qwen3-vl-flash-2026-01-22qwen3-vl-flash-latest
Legacy names still seen in some workloads:
qwen-vl-max-latestqwen-vl-plus-latest
For OCR-specialized extraction, prefer skills/ai/multimodal/alicloud-ai-multimodal-qwen-ocr/ instead of using the general VL skill.
Normalized interface (multimodal.chat)
Request
prompt(string, required): user question/instruction about image.image(string, required): HTTPS URL, local path, ordata:URL.model(string, optional): defaultqwen3-vl-plus.max_tokens(int, optional): default512.temperature(float, optional): default0.2.detail(string, optional):auto/low/high, defaultauto.json_mode(bool, optional): return JSON-only response when possible.schema(object, optional): JSON Schema for structured extraction.max_retries(int, optional): retry count for429/5xx, default2.retry_backoff_s(float, optional): exponential backoff base seconds, default1.5.
Response
text(string): primary model answer.model(string): model actually used.usage(object): token usage if returned by backend.
Quickstart
python skills/ai/multimodal/alicloud-ai-multimodal-qwen-vl/scripts/analyze_image.py \
--request '{"prompt":"Summarize the main content in this image","image":"https://example.com/demo.jpg"}' \
--print-responseUsing local image:
python skills/ai/multimodal/alicloud-ai-multimodal-qwen-vl/scripts/analyze_image.py \
--request '{"prompt":"Extract key information from the image","image":"./samples/invoice.png","model":"qwen3-vl-plus"}' \
--print-responseStructured extraction (JSON mode):
python skills/ai/multimodal/alicloud-ai-multimodal-qwen-vl/scripts/analyze_image.py \
--request '{"prompt":"Extract fields: title, amount, date","image":"./samples/invoice.png"}' \
--json-mode \
--print-responseStructured extraction (JSON Schema):
python skills/ai/multimodal/alicloud-ai-multimodal-qwen-vl/scripts/analyze_image.py \
--request '{"prompt":"Extract invoice fields","image":"./samples/invoice.png"}' \
--schema skills/ai/multimodal/alicloud-ai-multimodal-qwen-vl/references/examples/invoice.schema.json \
--print-responsecURL (compatible mode)
curl -sS https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model":"qwen3-vl-plus",
"messages":[
{
"role":"user",
"content":[
{"type":"image_url","image_url":{"url":"https://example.com/demo.jpg"}},
{"type":"text","text":"Describe this image and list executable actions"}
]
}
],
"max_tokens":512,
"temperature":0.2
}'Output location
- If
--outputis set, JSON response is saved to that file. - Default output dir convention:
output/alicloud-ai-multimodal-qwen-vl/.
Smoke test
python tests/ai/multimodal/alicloud-ai-multimodal-qwen-vl-test/scripts/smoke_test_qwen_vl.py \
--image ./tmp/vl_test_cat.pngError handling
| Error | Likely cause | Action |
|---|---|---|
| 401/403 | Missing or invalid key | Check DASHSCOPE_API_KEY and account permissions. |
| 400 | Invalid request schema or unsupported image source | Validate messages content and image URL/path format. |
| 429 | Rate limit | Retry with exponential backoff and lower concurrency. |
| 5xx | Temporary backend issue | Retry with backoff and idempotent request design. |
Operational guidance
- For stable production behavior, pin snapshot model IDs instead of pure
-latest. - Compress very large images before upload to reduce latency and cost.
- Add explicit extraction constraints in prompt (fields, JSON shape, language).
- For OCR-like output, ask for confidence notes and unresolved text markers.
Workflow
1) Confirm user intent, region, identifiers, and whether the operation is read-only or mutating. 2) Run one minimal read-only query first to verify connectivity and permissions. 3) Execute the target operation with explicit parameters and bounded scope. 4) Verify results and save output/evidence files.
References
- Source list:
references/sources.md - API notes:
references/api_reference.md
interface:
display_name: "Alibaba Cloud AI Multimodal Qwen VL"
short_description: "Image understanding with latest Qwen VL models"
default_prompt: "Use $alicloud-ai-multimodal-qwen-vl to complete this ai/multimodal task on Alibaba Cloud."
Qwen VL API Reference Notes
Endpoint (compatible mode)
- Domestic:
https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions - International:
https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions
Minimal request body
{
"model": "qwen3-vl-plus",
"messages": [
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "https://example.com/sample.jpg",
"detail": "auto"
}
},
{
"type": "text",
"text": "Describe the image and extract key entities."
}
]
}
],
"max_tokens": 512,
"temperature": 0.2
}Response extraction
choices[0].message.contentis the primary answer.usagecontains token statistics when provided.modelmay return canonical model ID even if alias is used.
Structured output options
- JSON mode:
{
"response_format": {
"type": "json_object"
}
}- JSON Schema mode:
{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "image_understanding_result",
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"amount": {"type": "number"}
},
"required": ["title"]
}
}
}
}Notes
- For deterministic extraction tasks, lower
temperature(for example0to0.2). - For production reproducibility, prefer pinned snapshot model IDs.
{
"type": "object",
"properties": {
"title": {
"type": "string"
},
"invoice_no": {
"type": "string"
},
"date": {
"type": "string"
},
"amount": {
"type": "number"
}
},
"required": ["title", "amount"]
}
Sources
- Alibaba Cloud DashScope model list (OpenAI compatible): https://help.aliyun.com/zh/model-studio/models
- Alibaba Cloud Qwen VL usage (OpenAI compatible examples): https://help.aliyun.com/zh/model-studio/qwen-vl?spm=a2c4g.11186623.help-menu-search-2400256.d_5_0_9_0.6dc14095CSwPw2
- Alibaba Cloud model rate limits (contains latest aliases/snapshots): https://help.aliyun.com/zh/model-studio/rate-limit
Last checked: 2026-02-25
#!/usr/bin/env python3
"""Analyze an image with Alibaba Cloud Model Studio Qwen VL models.
Usage:
python scripts/analyze_image.py --request '{"prompt":"...","image":"https://..."}'
python scripts/analyze_image.py --file request.json --print-response
"""
from __future__ import annotations
import argparse
import base64
import configparser
import json
import mimetypes
import os
import sys
import time
from pathlib import Path
from typing import Any
try:
import requests
except ImportError:
print("Error: requests is not installed. Run: pip install requests", file=sys.stderr)
sys.exit(1)
DEFAULT_MODEL = "qwen3-vl-plus"
DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
DEFAULT_MAX_TOKENS = 512
DEFAULT_TEMPERATURE = 0.2
DEFAULT_DETAIL = "auto"
DEFAULT_TIMEOUT_S = 120
DEFAULT_MAX_RETRIES = 2
DEFAULT_RETRY_BACKOFF_S = 1.5
RETRYABLE_STATUS = {429, 500, 502, 503, 504}
def _find_repo_root(start: Path) -> Path | None:
for parent in [start] + list(start.parents):
if (parent / ".git").exists():
return parent
return None
def _load_dotenv(path: Path) -> None:
if not path.exists():
return
for line in path.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = value
def _load_env() -> None:
_load_dotenv(Path.cwd() / ".env")
repo_root = _find_repo_root(Path(__file__).resolve())
if repo_root:
_load_dotenv(repo_root / ".env")
def _load_dashscope_api_key_from_credentials() -> None:
if os.environ.get("DASHSCOPE_API_KEY"):
return
credentials_path = Path(os.path.expanduser("~/.alibabacloud/credentials"))
if not credentials_path.exists():
return
config = configparser.ConfigParser()
try:
config.read(credentials_path)
except configparser.Error:
return
profile = os.getenv("ALIBABA_CLOUD_PROFILE") or os.getenv("ALICLOUD_PROFILE") or "default"
if not config.has_section(profile):
return
key = config.get(profile, "dashscope_api_key", fallback="").strip()
if not key:
key = config.get(profile, "DASHSCOPE_API_KEY", fallback="").strip()
if key:
os.environ["DASHSCOPE_API_KEY"] = key
def load_request(args: argparse.Namespace) -> dict[str, Any]:
if args.request:
return json.loads(args.request)
if args.file:
with open(args.file, "r", encoding="utf-8") as f:
return json.load(f)
raise ValueError("Either --request or --file must be provided")
def _path_to_data_url(path: Path) -> str:
data = path.read_bytes()
mime_type, _ = mimetypes.guess_type(path.name)
if not mime_type:
mime_type = "application/octet-stream"
encoded = base64.b64encode(data).decode("ascii")
return f"data:{mime_type};base64,{encoded}"
def resolve_image_input(image_value: str) -> str:
if image_value.startswith("http://") or image_value.startswith("https://"):
return image_value
if image_value.startswith("data:"):
return image_value
path = Path(image_value)
if path.exists() and path.is_file():
return _path_to_data_url(path)
return image_value
def extract_error_message(payload: Any) -> str:
if isinstance(payload, dict):
error = payload.get("error")
if isinstance(error, dict):
message = error.get("message")
if isinstance(message, str) and message.strip():
return message.strip()
message = payload.get("message")
if isinstance(message, str) and message.strip():
return message.strip()
return "Unknown error"
def extract_text_content(content: Any) -> str:
if isinstance(content, str):
return content
if isinstance(content, list):
for item in content:
if isinstance(item, dict) and item.get("type") == "text":
text = item.get("text")
if isinstance(text, str):
return text
return json.dumps(content, ensure_ascii=True)
if content is None:
return ""
return json.dumps(content, ensure_ascii=True)
def try_parse_json_text(text: str) -> Any | None:
text = text.strip()
if not text:
return None
try:
return json.loads(text)
except json.JSONDecodeError:
return None
def build_payload(
req: dict[str, Any],
model: str,
image_url: str,
detail: str,
json_mode: bool,
schema_obj: dict[str, Any] | None,
) -> dict[str, Any]:
prompt = req["prompt"]
if schema_obj:
prompt = (
f"{prompt}\n\n"
"Return ONLY JSON that matches the provided schema. "
"Do not include markdown or extra commentary."
)
elif json_mode:
prompt = f"{prompt}\n\nReturn ONLY valid JSON."
payload: dict[str, Any] = {
"model": model,
"messages": [
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": image_url, "detail": detail}},
{"type": "text", "text": prompt},
],
}
],
"max_tokens": req.get("max_tokens", DEFAULT_MAX_TOKENS),
"temperature": req.get("temperature", DEFAULT_TEMPERATURE),
}
if schema_obj:
payload["response_format"] = {
"type": "json_schema",
"json_schema": {
"name": "image_understanding_result",
"schema": schema_obj,
},
}
elif json_mode:
payload["response_format"] = {"type": "json_object"}
return payload
def _post_with_retry(
url: str,
headers: dict[str, str],
payload: dict[str, Any],
timeout_s: int,
max_retries: int,
retry_backoff_s: float,
) -> requests.Response:
last_error: str | None = None
for attempt in range(max_retries + 1):
response = requests.post(url, headers=headers, json=payload, timeout=timeout_s)
if response.status_code < 400:
return response
body: dict[str, Any] | None = None
try:
body = response.json()
except ValueError:
body = None
error_message = extract_error_message(body) if body is not None else response.text[:400]
last_error = f"HTTP {response.status_code}: {error_message}"
if response.status_code not in RETRYABLE_STATUS or attempt >= max_retries:
raise RuntimeError(last_error)
time.sleep(retry_backoff_s * (2**attempt))
raise RuntimeError(last_error or "Request failed after retries")
def call_analyze(req: dict[str, Any]) -> dict[str, Any]:
prompt = req.get("prompt")
image = req.get("image")
if not prompt:
raise ValueError("prompt is required")
if not image:
raise ValueError("image is required")
model = req.get("model", DEFAULT_MODEL)
base_url = req.get("base_url", DEFAULT_BASE_URL).rstrip("/")
image_url = resolve_image_input(image)
detail = req.get("detail", DEFAULT_DETAIL)
json_mode = bool(req.get("json_mode", False))
schema_obj = req.get("schema")
if schema_obj is not None and not isinstance(schema_obj, dict):
raise ValueError("schema must be a JSON object")
payload = build_payload(
req=req,
model=model,
image_url=image_url,
detail=detail,
json_mode=json_mode,
schema_obj=schema_obj,
)
response = _post_with_retry(
url=f"{base_url}/chat/completions",
headers={
"Authorization": f"Bearer {os.environ['DASHSCOPE_API_KEY']}",
"Content-Type": "application/json",
},
payload=payload,
timeout_s=int(req.get("timeout_s", DEFAULT_TIMEOUT_S)),
max_retries=int(req.get("max_retries", DEFAULT_MAX_RETRIES)),
retry_backoff_s=float(req.get("retry_backoff_s", DEFAULT_RETRY_BACKOFF_S)),
)
data = response.json()
choices = data.get("choices") or []
if not choices:
raise RuntimeError("No choices returned by DashScope")
message = choices[0].get("message") or {}
content = message.get("content")
text = extract_text_content(content)
parsed_json = try_parse_json_text(text) if (json_mode or schema_obj is not None) else None
result = {
"text": text,
"model": data.get("model", model),
"usage": data.get("usage", {}),
}
if parsed_json is not None:
result["json"] = parsed_json
return result
def main() -> None:
parser = argparse.ArgumentParser(description="Analyze image with qwen3-vl-plus")
parser.add_argument("--request", help="Inline JSON request string")
parser.add_argument("--file", help="Path to JSON request file")
parser.add_argument("--json-mode", action="store_true", help="Request JSON-only output")
parser.add_argument("--schema", default="", help="Path to JSON Schema file for structured output")
parser.add_argument(
"--output",
default="",
help="Optional output JSON path, e.g. output/ai-multimodal-qwen-vl/result.json",
)
parser.add_argument("--max-retries", type=int, default=DEFAULT_MAX_RETRIES, help="Retry count for 429/5xx")
parser.add_argument(
"--retry-backoff-s",
type=float,
default=DEFAULT_RETRY_BACKOFF_S,
help="Base retry backoff seconds (exponential)",
)
parser.add_argument("--print-response", action="store_true", help="Print normalized response JSON")
args = parser.parse_args()
_load_env()
_load_dashscope_api_key_from_credentials()
if not os.environ.get("DASHSCOPE_API_KEY"):
print(
"Error: DASHSCOPE_API_KEY is not set. Configure it via env/.env or ~/.alibabacloud/credentials.",
file=sys.stderr,
)
print("Example .env:\n DASHSCOPE_API_KEY=your_key_here", file=sys.stderr)
print(
"Example credentials:\n [default]\n dashscope_api_key=your_key_here",
file=sys.stderr,
)
sys.exit(1)
req = load_request(args)
req["max_retries"] = args.max_retries
req["retry_backoff_s"] = args.retry_backoff_s
if args.json_mode:
req["json_mode"] = True
if args.schema:
schema_path = Path(args.schema)
req["schema"] = json.loads(schema_path.read_text(encoding="utf-8"))
result = call_analyze(req)
if args.output:
output_path = Path(args.output)
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(json.dumps(result, ensure_ascii=True, indent=2), encoding="utf-8")
if args.print_response:
print(json.dumps(result, ensure_ascii=True))
if __name__ == "__main__":
main()
Related skills
How it compares
Pick alicloud-ai-multimodal-qwen-vl over generic LLM skills when the workload requires Alibaba Cloud DashScope vision APIs rather than text-only models.
FAQ
Which Qwen-VL models does alicloud-ai-multimodal-qwen-vl support?
alicloud-ai-multimodal-qwen-vl documents the Qwen3 VL family, defaulting to qwen3-vl-plus with qwen3-vl-flash as a faster alias. The skill routes image Q&A, chart reading, and screenshot understanding through DashScope-compatible multimodal.chat calls.
What authentication does alicloud-ai-multimodal-qwen-vl require?
alicloud-ai-multimodal-qwen-vl expects DASHSCOPE_API_KEY in the environment or dashscope_api_key in ~/.alibabacloud/credentials. Legacy ALIBABA_CLOUD_* and ALICLOUD_* aliases are also accepted by runtime scripts in the parent repo.
How do you run a quick Qwen-VL image test?
alicloud-ai-multimodal-qwen-vl includes analyze_image.py, invoked with a JSON request containing prompt and image fields. The script prints results to stdout and can persist raw and normalized response files for traceability.