1 line
34 KiB
JSON
1 line
34 KiB
JSON
{"repo": "baklib-tools/skills", "files": {"examples/requirements-to-published-content-health-cms/artifacts/images/README.md": {"f0df7b92f1a53c54a6bfb7cd8320cc2fc03db76c": {"t": 1785596113.6021707, "n": 903, "h": "196ee6f38605e6bd", "c": "# 示例配图(三镜头)\n\n与 [image-plan-from-content.md](../image-plan-from-content.md) 及 [prompts/](prompts/) 中提示词对应:\n\n| 文件 | 镜头 |\n|------|------|\n| `rtc-demo-shot1.png` | 痛点:内容分散、多屏混乱 |\n| `rtc-demo-shot2.png` | 方案:统一内容枢纽 |\n| `rtc-demo-shot3.png` | 结果/CTA:分期路径 |\n\n[06-wechat-article.html](../06-wechat-article.html) 使用相对路径 `images/rtc-demo-shot*.png` 引用;本地用浏览器打开该 HTML 即可预览。\n\n## 重新生成(维护者)\n\n在仓库根目录 `skills`(含 `.config/UCLOUD_API_KEY`)下执行:\n\n```bash\npython3 skills/image-generation-ucloud/scripts/generate_ucloud_image.py \\\n --prompt examples/requirements-to-published-content-health-cms/artifacts/images/prompts/shot1-pain.txt \\\n --output examples/requirements-to-published-content-health-cms/artifacts/images/rtc-demo-shot1.png \\\n --size 4:3 --image-size 1K\n```\n\n将 `shot1-pain` / `shot2-solution` / `shot3-cta` 与输出文件名轮换执行三次。模型与密钥说明见 [image-generation-ucloud](../../../../skills/image-generation-ucloud/SKILL.md)。\n"}}, "skills/image-generation/SKILL.md": {"f0df7b92f1a53c54a6bfb7cd8320cc2fc03db76c": {"t": 1785596113.791131, "n": 3524, "h": "6aef566ba6b21018", "c": "---\nname: image-generation\ndescription: >-\n 图像生成工作流总览:何时出图、如何选用后端子技能、与提示词技能配合。\n 在用户需要配图/示意图、选择 UCloud 或 OpenRouter 等 API 出图、或询问本仓库「出图技能怎么组合」时使用。\n---\n\n# 图像生成(总技能)\n\n## 执行前提:须配合服务商子技能\n\n本总技能**不能**单独完成「调 API、运行脚本、产出图像」;只说明工作流、组合方式与密钥检查原则,**不包含**任一服务商的完整端点、请求体与可执行命令。\n\n- **必须**与至少一个 **`image-generation-<服务商>`** 子技能同时使用(当前为 **`image-generation-ucloud`**、**`image-generation-openrouter`**;以后仓库若增加其它供应商,命名约定见下表)。实际出图时以**对应子技能**的 `SKILL.md` 与目录内脚本为准。\n- 若环境中**仅有**本总技能、**没有**任一端子技能,代理应**先**请用户选定或说明打算使用的后端(或根据已有密钥推断),再打开该子技能;**不要**仅凭本文件自行拼凑 HTTP 调用。\n- **提示词类技能**(如 `nano-banana-pro-prompting`)可与总技能、服务商子技能组合,但**不能**替代服务商子技能完成鉴权与请求。\n\n## 命名与组合规则(开源场景)\n\n本仓库将「出图」拆成 **1 个总技能 + 若干后端子技能**,避免把某一云厂商或某一账号流程写死为唯一路径。\n\n| 类型 | 目录名(`name`) | 何时阅读 |\n|------|------------------|----------|\n| 总览 | `image-generation` | 工作流、授权、与提示词技能配合、选哪个后端 |\n| UCloud ModelVerse | `image-generation-ucloud` | 使用 UCloud 文档中的 ModelVerse 图像 API |\n| OpenRouter | `image-generation-openrouter` | 使用 OpenRouter 调用 Gemini 等图像能力 |\n\n**约定**:以后若增加其他供应商,使用同一前缀 `image-generation-` + 短后缀(如 `image-generation-xxx`),并在本文件表格中补充一行。\n\n## 本技能做什么\n\n- 说明 **何时** 适合调用出图能力(含与正文/营销文案的配合方式)。\n- 指引 **下一步** 应打开哪个子技能(按用户实际使用的接口)。\n- 指向本仓库已有的 **提示词专项技能**(见下文),与「如何调 HTTP」解耦。\n\n## 本技能不做什么\n\n- **不**作为可独立执行的出图技能:端点、模型 ID、脚本路径与参数以各 **`image-generation-*` 服务商子技能**为准,本文件不重复罗列以免与子技能脱节。\n- **不**代替用户保管或填写密钥、**不**承诺计费与配额;密钥与费用以各平台控制台为准。\n- **不**把某一项目的私有目录、内部规范文件名写死为唯一路径;你应在**自己的项目**里维护风格说明与占位约定。\n- 提示词撰写原则见 `nano-banana-pro-prompting`;**单次 API 请求的提示词须自包含**,勿假设模型能「记住上一张图」(见该技能与 [AGENTS.md](https://github.com/baklib-tools/skills/blob/main/AGENTS.md))。\n\n## 与提示词技能的关系\n\n- **撰写/优化** Nano Banana(Gemini 3 Pro Image)类提示词:使用 **[nano-banana-pro-prompting](https://github.com/baklib-tools/skills/blob/main/skills/nano-banana-pro-prompting/SKILL.md)**。\n- **调用哪个 HTTP 接口**:按后端阅读 **`image-generation-ucloud`** 或 **`image-generation-openrouter`**;二者与提示词技能正交,可任意组合。\n\n## 执行前检查 API 密钥(代理必读)\n\n在运行各服务商脚本或替用户发起出图请求之前,**先确认**对应密钥是否已配置;若未配置,**不要静默失败**,应提示用户**优先**在**项目根目录**下创建 **`.config/`** 内密钥文件(再备选环境变量):\n\n| 后端 | 优先(项目根) | 备选(环境变量) |\n|------|----------------|------------------|\n| UCloud | `.config/UCLOUD_API_KEY` | `UCLOUD_API_KEY` |\n| OpenRouter | `.config/OPENROUTER_API_KEY` | `OPENROUTER_API_KEY` |\n\n文件内容为**单行**密钥文本,UTF-8 编码;**勿**将密钥写入仓库或技能正文。脚本会从项目根向上查找含 `.config` 的目录(详见各子技能说明)。\n\n## 工作流建议(通用)\n\n1. **先定模型与风格**(是否用 Gemini 3 Pro Image、是否需图内文字等),再写提示词文件(UTF-8)。\n2. **再选后端**:有 UCloud ModelVerse 密钥则读 `image-generation-ucloud`;若使用 OpenRouter 则读 `image-generation-openrouter`。\n3. **再执行**:由用户或 CI 在本地运行其脚本/工具发起请求;代理可协助检查命令行参数与错误信息含义,但**不**默认代跑生产密钥请求(除非你明确授权且环境已配置)。\n\n### 与「文章成稿」类任务的配合\n\n- 若正文中仅有配图占位,**不要默认自动出图**;在用户**明确**要求生成配图或等价指令后再执行出图与替换占位。\n- 若任务本身已包含「生成配图」「成片」等授权,可按该任务技能执行,仍建议 **Nano Banana + 选定后端** 与项目规范一致。\n\n## 参考脚本(按服务商拆分)\n\n各云厂商的 **模型 ID、参数、请求体** 差异大,本仓库将 **Python 示例脚本放在对应子技能目录**,避免一个脚本里堆满互斥逻辑:\n\n| 服务商 | 脚本路径 | 依赖 |\n|--------|----------|------|\n| UCloud ModelVerse | [`generate_ucloud_image.py`](https://github.com/baklib-tools/skills/blob/main/skills/image-generation-ucloud/scripts/generate_ucloud_image.py) | `requests` |\n| OpenRouter | [`generate_openrouter_image.py`](https://github.com/baklib-tools/skills/blob/main/skills/image-generation-openrouter/scripts/generate_openrouter_image.py) | `requests` |\n\n将对应技能目录安装到你项目中的**技能路径**(以所用工具文档为准)后,在项目根执行;脚本通过向上查找含 **`.config`** 的目录定位密钥文件,详见各脚本 `--help`。本仓库**不提供**本地离线出图示例脚本(能力因环境差异大,由用户自行选用工具链)。\n\n**总技能目录不再提供**「三合一」聚合脚本;换后端时换目录与脚本即可。\n\n## 失败时\n\n- 优先根据 **stderr 与各平台返回** 判断是鉴权、模型名、尺寸还是限流。\n- 可在 **不泄露密钥** 的前提下,切换到另一后端子技能中的流程(例如 UCloud 不可用时改用 OpenRouter),并相应调整模型与尺寸参数。\n"}}, "skills/image-generation-ucloud/scripts/generate_ucloud_image.py": {"f0df7b92f1a53c54a6bfb7cd8320cc2fc03db76c": {"t": 1785596113.8233624, "n": 14602, "h": "6d6f4629f44b014a", "c": "#!/usr/bin/env python3\n\"\"\"\nUCloud ModelVerse 图像生成(参考实现)\n\n仅处理 UCloud 文档中的模型与参数;模型 ID、尺寸别名以 UCloud 当前文档为准。\n依赖:requests。将本技能目录安装到项目的技能路径(以所用工具文档为准)后,在项目根执行。\n\n密钥(读取顺序):优先项目根 .config/UCLOUD_API_KEY,其次环境变量 UCLOUD_API_KEY。\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport base64\nimport json\nimport os\nimport sys\nfrom pathlib import Path\nfrom typing import Literal, TypedDict\n\nimport requests\n\n# 官方模型列表(无需密钥即可查询,用于核对 baseModelId)\nUCLOUD_V1BETA_MODELS_URL = \"https://api.modelverse.cn/v1beta/models\"\nUCLOUD_V1_MODELS_URL = \"https://api.modelverse.cn/v1/models\"\n\n\nclass _ModelImagesGenerations(TypedDict):\n kind: Literal[\"images_generations\"]\n sizes: dict[str, str]\n\n\nclass _ModelGenerateContent(TypedDict):\n kind: Literal[\"generate_content\"]\n sizes: None # 无像素别名表;--size 直接作 aspectRatio\n\n\nModelConfig = _ModelImagesGenerations | _ModelGenerateContent\n\n# 仅保留:豆包文生图(images/generations)+ Gemini 图像(generateContent)。\n# Gemini 模型 ID 请与 GET v1beta/models 返回的 baseModelId 对齐(例如 Flash 图像为 gemini-3.1-flash-image)。\nUCLOUD_MODELS: dict[str, ModelConfig] = {\n \"doubao-seedream-4.5\": {\n \"kind\": \"images_generations\",\n \"sizes\": {\n \"4:3\": \"2304x1728\",\n \"3:4\": \"1728x2304\",\n \"16:9\": \"2560x1440\",\n \"9:16\": \"1440x2560\",\n },\n },\n \"gemini-3-pro-image\": {\n \"kind\": \"generate_content\",\n \"sizes\": None,\n },\n \"gemini-3.1-flash-image\": {\n \"kind\": \"generate_content\",\n \"sizes\": None,\n },\n}\n\nDEFAULT_MODEL = \"gemini-3-pro-image\"\n\n\ndef find_project_root() -> Path:\n script = Path(__file__).resolve()\n home = Path.home()\n p = script.parent\n for _ in range(10):\n if (p / \".config\").is_dir() and p != home:\n return p\n if p == p.parent:\n break\n p = p.parent\n for depth in (4, 3):\n if len(script.parents) > depth:\n return script.parents[depth]\n return script.parents[-1]\n\n\ndef get_api_key(env_name: str, config_filename: str) -> str | None:\n project_root = find_project_root()\n config_file = project_root / \".config\" / config_filename\n if config_file.exists():\n try:\n with open(config_file, \"r\", encoding=\"utf-8\") as f:\n key = f.read().strip()\n if key:\n return key\n except OSError as e:\n print(f\"⚠️ 读取配置文件失败: {e}\", file=sys.stderr)\n api_key = os.getenv(env_name)\n if api_key:\n return api_key.strip()\n return None\n\n\ndef get_ucloud_api_key() -> str | None:\n return get_api_key(\"UCLOUD_API_KEY\", \"UCLOUD_API_KEY\")\n\n\ndef print_ucloud_api_key_help() -> None:\n print(\"\", file=sys.stderr)\n print(\"📌 未检测到 UCloud API 密钥,请任选其一:\", file=sys.stderr)\n print(\n \" • 优先:在项目根目录创建 .config/UCLOUD_API_KEY(单行密钥,UTF-8)\",\n file=sys.stderr,\n )\n print(\n \" • 备选:环境变量 UCLOUD_API_KEY\",\n file=sys.stderr,\n )\n print(\n \" • 换后端:OpenRouter 技能目录下脚本(image-generation-openrouter)\",\n file=sys.stderr,\n )\n print(\n \" 技能说明:image-generation-ucloud(本仓库 skills/ 下)\",\n file=sys.stderr,\n )\n\n\ndef resolve_canonical_model_id(user_arg: str) -> str | None:\n \"\"\"将 CLI 传入的模型名解析为 UCLOUD_MODELS 的键(兼容旧名与 API 长 ID)。\"\"\"\n u = user_arg.strip()\n if u in UCLOUD_MODELS:\n return u\n if \"doubao-seedream\" in u:\n return \"doubao-seedream-4.5\"\n # API 长名 -> 本脚本使用的短名\n if u == \"publishers/google/models/gemini-3-pro-image-preview\":\n return \"gemini-3-pro-image\"\n if u == \"publishers/google/models/gemini-3-pro-image\":\n return \"gemini-3-pro-image\"\n # 含 gemini-3.1-flash-image 即视为 Flash 图像(兼容 -preview 后缀)\n if \"gemini-3.1-flash-image\" in u:\n return \"gemini-3.1-flash-image\"\n if \"gemini-3-pro-image\" in u:\n return \"gemini-3-pro-image\"\n return None\n\n\ndef fetch_v1beta_models() -> list[dict[str, object]]:\n r = requests.get(UCLOUD_V1BETA_MODELS_URL, timeout=30)\n r.raise_for_status()\n data = r.json()\n return list(data.get(\"models\") or [])\n\n\ndef print_official_model_lists() -> None:\n \"\"\"打印官方 API 返回的模型 ID(走 stdout,便于重定向)。\"\"\"\n print(\"—— GET v1beta/models(Gemini 协议,含 generateContent,节选 id 含 image)——\")\n try:\n models = fetch_v1beta_models()\n except requests.RequestException as e:\n print(f\"❌ 请求失败: {e}\", file=sys.stderr)\n return\n for m in models:\n bid = str(m.get(\"baseModelId\", \"\"))\n actions = m.get(\"supportedActions\") or []\n if \"generateContent\" not in actions:\n continue\n if \"image\" not in bid.lower():\n continue\n print(f\" {bid}\")\n\n print()\n print(\"—— GET v1/models(OpenAI 兼容列表,节选 id 含 image)——\")\n try:\n r = requests.get(UCLOUD_V1_MODELS_URL, timeout=30)\n r.raise_for_status()\n data = r.json()\n for row in data.get(\"data\") or []:\n mid = str(row.get(\"id\", \"\"))\n if \"image\" in mid.lower():\n print(f\" {mid}\")\n except requests.RequestException as e:\n print(f\"❌ v1/models 请求失败: {e}\", file=sys.stderr)\n\n print()\n print(\n \"本脚本内置文生图模型(见 UCLOUD_MODELS):\"\n f\" {', '.join(UCLOUD_MODELS)}。\"\n \" 豆包走 v1/images/generations,通常不在 v1beta 列表中,以 UCloud 图像 API 文档为准。\"\n )\n\n\ndef resolve_api_size(sizes: dict[str, str] | None, size: str | None) -> str | None:\n \"\"\"根据该模型的别名表解析 --size;无表则原样返回(或 None)。\"\"\"\n if not size:\n return None\n if sizes is None:\n return size\n resolved = sizes.get(size, size)\n if size in sizes:\n print(f\" 尺寸别名 {size!r} → {resolved}\", file=sys.stderr)\n return resolved\n\n\ndef _save_image_from_response(image_data: dict, output_path: Path) -> bool:\n if \"url\" in image_data:\n img_response = requests.get(image_data[\"url\"], timeout=30)\n if img_response.status_code != 200:\n print(f\"❌ 下载图像失败: HTTP {img_response.status_code}\", file=sys.stderr)\n return False\n output_path.write_bytes(img_response.content)\n print(f\"✅ 图像已保存: {output_path}\", file=sys.stderr)\n return True\n if \"b64_json\" in image_data:\n image_bytes = base64.b64decode(image_data[\"b64_json\"])\n output_path.write_bytes(image_bytes)\n print(f\"✅ 图像已保存: {output_path}\", file=sys.stderr)\n return True\n print(\"❌ 错误: 响应中未找到图像数据\", file=sys.stderr)\n return False\n\n\ndef _request_images_generations(\n api_key: str,\n payload: dict,\n output_file: str,\n model_id: str,\n prompt: str,\n size_hint: str | None = None,\n) -> bool:\n url = \"https://api.modelverse.cn/v1/images/generations\"\n headers = {\"Authorization\": api_key, \"Content-Type\": \"application/json\"}\n print(\"🎨 正在生成图像...\", file=sys.stderr)\n print(f\" 模型: {model_id}\", file=sys.stderr)\n print(f\" 提示词: {prompt}\", file=sys.stderr)\n if size_hint:\n print(f\" 尺寸: {size_hint}\", file=sys.stderr)\n try:\n response = requests.post(url, json=payload, headers=headers, timeout=60)\n except requests.exceptions.RequestException as e:\n print(f\"❌ 网络请求失败: {e}\", file=sys.stderr)\n return False\n if response.status_code != 200:\n print(f\"❌ API 请求失败: HTTP {response.status_code}\", file=sys.stderr)\n print(f\" 响应: {response.text}\", file=sys.stderr)\n print_ucloud_api_key_help()\n return False\n result = response.json()\n if \"data\" not in result or not result[\"data\"]:\n print(\"❌ 错误: API 响应格式不正确\", file=sys.stderr)\n print(f\" 响应: {result}\", file=sys.stderr)\n return False\n output_path = Path(output_file)\n output_path.parent.mkdir(parents=True, exist_ok=True)\n return _save_image_from_response(result[\"data\"][0], output_path)\n\n\ndef generate_via_images_generations(\n model_id: str,\n prompt: str,\n output_file: str,\n size: str | None = None,\n n: int = 1,\n image_url: str | None = None,\n) -> bool:\n cfg = UCLOUD_MODELS.get(model_id)\n if not cfg or cfg[\"kind\"] != \"images_generations\":\n return False\n sizes = cfg[\"sizes\"]\n api_key = get_ucloud_api_key()\n if not api_key:\n print(\"❌ 错误: 未找到 UCloud API 密钥\", file=sys.stderr)\n print_ucloud_api_key_help()\n return False\n api_size = resolve_api_size(sizes, size)\n payload = {\n \"model\": model_id,\n \"prompt\": prompt,\n \"n\": n,\n \"watermark\": False,\n }\n if api_size:\n payload[\"size\"] = api_size\n if image_url:\n payload[\"image\"] = image_url\n return _request_images_generations(\n api_key, payload, output_file, model_id, prompt, api_size\n )\n\n\ndef generate_gemini_generate_content(\n prompt: str,\n output_file: str,\n model_id: str,\n size: str | None = None,\n image_size: str = \"1K\",\n) -> bool:\n \"\"\"v1beta generateContent,适用于文档中的 Gemini 图像模型 ID。\"\"\"\n api_key = get_ucloud_api_key()\n if not api_key:\n print(\"❌ 错误: 未找到 UCloud API 密钥\", file=sys.stderr)\n print_ucloud_api_key_help()\n return False\n\n aspect_ratio = \"4:3\"\n if size:\n aspect_ratio = size\n\n url = f\"https://api.modelverse.cn/v1beta/models/{model_id}:generateContent\"\n headers = {\n \"x-goog-api-key\": api_key,\n \"Content-Type\": \"application/json\",\n }\n payload = {\n \"contents\": [{\"role\": \"user\", \"parts\": [{\"text\": prompt}]}],\n \"generationConfig\": {\n \"responseModalities\": [\"IMAGE\"],\n \"imageConfig\": {\n \"aspectRatio\": aspect_ratio,\n \"imageSize\": image_size,\n },\n },\n }\n\n label = \"Gemini 图像\"\n if \"flash\" in model_id.lower():\n label = \"Gemini 3.1 Flash Image\"\n elif \"pro-image\" in model_id.lower():\n label = \"Gemini 3 Pro Image\"\n\n print(f\"🎨 正在使用 UCloud {label} 生成图像...\", file=sys.stderr)\n print(f\" 模型: {model_id}\", file=sys.stderr)\n print(f\" 提示词: {prompt}\", file=sys.stderr)\n print(f\" 宽高比: {aspect_ratio}, 分辨率: {image_size}\", file=sys.stderr)\n\n try:\n response = requests.post(url, json=payload, headers=headers, timeout=300)\n except requests.exceptions.RequestException as e:\n print(f\"❌ 网络请求失败: {e}\", file=sys.stderr)\n return False\n\n if response.status_code != 200:\n print(f\"❌ API 请求失败: HTTP {response.status_code}\", file=sys.stderr)\n try:\n error_data = response.json()\n print(\n f\" 错误详情: {json.dumps(error_data, indent=2, ensure_ascii=False)}\",\n file=sys.stderr,\n )\n except Exception:\n print(f\" 响应: {response.text}\", file=sys.stderr)\n print_ucloud_api_key_help()\n return False\n\n result = response.json()\n candidates = result.get(\"candidates\") or []\n if not candidates:\n print(\"❌ 错误: 响应中未找到 candidates\", file=sys.stderr)\n print(\n f\" 响应: {json.dumps(result, indent=2, ensure_ascii=False)}\",\n file=sys.stderr,\n )\n return False\n\n content = candidates[0].get(\"content\") or {}\n parts = content.get(\"parts\") or []\n\n inline_data = None\n mime_type = \"image/png\"\n for part in parts:\n if not isinstance(part, dict):\n continue\n data_block = part.get(\"inlineData\")\n if isinstance(data_block, dict) and \"data\" in data_block:\n inline_data = data_block[\"data\"]\n mime_type = data_block.get(\"mimeType\", mime_type)\n break\n\n if not inline_data:\n print(\"❌ 错误: 响应中未找到 inlineData 图像数据\", file=sys.stderr)\n print(\n f\" 响应: {json.dumps(result, indent=2, ensure_ascii=False)}\",\n file=sys.stderr,\n )\n return False\n\n try:\n image_bytes = base64.b64decode(inline_data)\n output_path = Path(output_file)\n output_path.parent.mkdir(parents=True, exist_ok=True)\n output_path.write_bytes(image_bytes)\n print(f\"✅ 图像已保存: {output_path}\", file=sys.stderr)\n print(f\" 格式: {mime_type}\", file=sys.stderr)\n return True\n except OSError as e:\n print(f\"❌ 保存图像失败: {e}\", file=sys.stderr)\n return False\n\n\ndef main() -> None:\n parser = argparse.ArgumentParser(\n description=\"UCloud ModelVerse 图像生成\",\n formatter_class=argparse.RawDescriptionHelpFormatter,\n epilog=\"\"\"\n示例:\n %(prog)s --list-models\n %(prog)s --prompt prompt.txt --output out.png --size 4:3\n %(prog)s --prompt prompt.txt --output out.png --model gemini-3.1-flash-image --size 16:9\n %(prog)s --prompt prompt.txt --output out.jpg --model doubao-seedream-4.5 --size 4:3\n \"\"\",\n )\n parser.add_argument(\n \"--list-models\",\n action=\"store_true\",\n help=\"调用 GET v1beta/models 与 GET v1/models,列出官方返回的图像相关模型 ID(无需密钥)\",\n )\n parser.add_argument(\"--prompt\", help=\"提示词文件路径(UTF-8)\")\n parser.add_argument(\"--output\", help=\"输出图像路径\")\n parser.add_argument(\n \"--model\",\n default=DEFAULT_MODEL,\n help=(\n f\"模型 ID。默认 {DEFAULT_MODEL};\"\n \"可选 gemini-3.1-flash-image、doubao-seedream-4.5;\"\n \"与官方不一致时请运行 --list-models 核对\"\n ),\n )\n parser.add_argument(\n \"--size\",\n default=None,\n metavar=\"比例或像素\",\n help=\"宽高比(如 4:3)或文档要求的像素串;Gemini 类为 aspectRatio\",\n )\n parser.add_argument(\n \"--image-size\",\n default=\"1K\",\n metavar=\"1K|2K|…\",\n help=\"仅 Gemini generateContent:generationConfig.imageConfig.imageSize(默认 1K)\",\n )\n\n args = parser.parse_args()\n\n if args.list_models:\n print_official_model_lists()\n sys.exit(0)\n\n if not args.prompt or not args.output:\n parser.error(\"请提供 --prompt 与 --output,或使用 --list-models\")\n\n if not get_ucloud_api_key():\n print(\"❌ 错误: 未找到 UCloud API 密钥\", file=sys.stderr)\n print_ucloud_api_key_help()\n sys.exit(1)\n\n try:\n prompt = Path(args.prompt).read_text(encoding=\"utf-8\")\n except OSError as e:\n print(f\"❌ 无法读取提示词文件 {args.prompt}: {e}\", file=sys.stderr)\n sys.exit(1)\n\n mid = resolve_canonical_model_id(args.model)\n if not mid:\n print(\n f\"❌ 不支持的模型: {args.model}。内置:{', '.join(UCLOUD_MODELS)}\",\n file=sys.stderr,\n )\n sys.exit(1)\n\n cfg = UCLOUD_MODELS[mid]\n if cfg[\"kind\"] == \"images_generations\":\n ok = generate_via_images_generations(\n mid, prompt, args.output, size=args.size, n=1\n )\n else:\n ok = generate_gemini_generate_content(\n prompt,\n args.output,\n mid,\n size=args.size,\n image_size=args.image_size,\n )\n\n sys.exit(0 if ok else 1)\n\n\nif __name__ == \"__main__\":\n main()\n"}}, "skills/image-generation-ucloud/SKILL.md": {"f0df7b92f1a53c54a6bfb7cd8320cc2fc03db76c": {"t": 1785596114.3689318, "n": 4120, "h": "acc8d1464fcc9bb7", "c": "---\nname: image-generation-ucloud\ndescription: >-\n 通过 UCloud ModelVerse 图像 API 出图:鉴权、端点、模型与尺寸约定。\n 在用户指定 UCloud、ModelVerse、api.modelverse.cn 或需 doubao-seedream / gemini-3-pro-image / gemini-3.1-flash-image 时使用。\n---\n\n# 图像生成 · UCloud(ModelVerse)\n\n## 技能范围\n\n- **本技能只解决**:如何按 UCloud 公开文档调用 **ModelVerse** 图像相关接口(鉴权位置、端点形态、模型与参数要点)。\n- **不包含**:替代用户执行扣费请求、保证 SLA;亦不重复 **提示词写法**(见 [nano-banana-pro-prompting](https://github.com/baklib-tools/skills/blob/main/skills/nano-banana-pro-prompting/SKILL.md))。\n\n## 执行前检查 API 密钥\n\n在运行 `generate_ucloud_image.py` 或代用户发起请求前,**先确认**已具备 UCloud 密钥;若缺失,提示用户**优先**在**项目根**创建 **`.config/UCLOUD_API_KEY`**(单行文本,UTF-8),其次再考虑环境变量 **`UCLOUD_API_KEY`**。本仓库示例脚本在读取提示词前会校验密钥,缺失时按上述顺序提示。\n\n## 官方文档与模型列表 API\n\n- 产品与接口说明以 UCloud 文档为准,例如文档中心内 **ModelVerse** 各模型「图像 API」章节(路径可能随版本调整,请站内搜索模型名)。\n- **Gemini 3.1 Flash Image**:[gemini-3.1-flash-image](https://docs.ucloud.cn/modelverse/api_doc/image_api/gemini-3.1-flash-image)(文档路径名可能与 **API 返回的 `baseModelId`** 不完全一致,以接口为准。)\n- **Gemini 3 Pro Image(示例)**:[gemini-3-pro-image](https://docs.ucloud.cn/modelverse/api_doc/image_api/gemini-3-pro-image)\n- **核对模型 ID(无需密钥)**:\n - `GET https://api.modelverse.cn/v1beta/models` — Gemini 协议,`models[].baseModelId`(如 `gemini-3.1-flash-image`)。\n - `GET https://api.modelverse.cn/v1/models` — OpenAI 兼容列表,`data[].id`。\n- 本仓库脚本支持 **`--list-models`**,会请求上述接口并节选名称中含 `image` 的条目,便于与脚本内置 ID 对照。\n\n## 鉴权与配置\n\n- **密钥**:使用你在 UCloud 控制台获得的 **ModelVerse API Key**。**推荐**:项目根 **`.config/UCLOUD_API_KEY`**(单行文本,UTF-8);**备选**:环境变量 **`UCLOUD_API_KEY`**。本仓库示例脚本的读取顺序为 **`.config` 文件优先于环境变量**。\n- **勿**在技能或仓库中写入真实密钥。\n\n## 两种 HTTP 形态(需按模型选用)\n\n### 1)通用图像生成(`images/generations`)\n\n- **典型用途**:`doubao-seedream-4.5` 等文档中约定走该路径的模型(豆包文生图;**模型 ID 以文档/控制台为准**,不一定出现在 `v1beta/models` 列表中)。\n- **端点(示例)**:`POST https://api.modelverse.cn/v1/images/generations`\n- **请求头**:`Authorization` 为 API Key;`Content-Type: application/json`。\n- **响应**:常见为 JSON,`data[0]` 中含 `url` 或 `b64_json`;下载或解码后写入文件即可。\n- **水印**:若文档支持 `watermark` 字段,按文档与合规要求设置。\n\n### 2)Gemini 系列图像 · `generateContent`(v1beta)\n\n- **典型模型 ID(示例)**:`gemini-3-pro-image`、`gemini-3.1-flash-image`(与 `GET v1beta/models` 的 `baseModelId` 对齐;亦可能出现 `publishers/google/models/gemini-3-pro-image` 等长名,以接口为准)。\n- **端点形态(示例)**:`POST https://api.modelverse.cn/v1beta/models/<model-id>:generateContent`\n- **请求头**:文档若要求使用 **`x-goog-api-key`** 传递密钥,则与上一种 `Authorization` 方式区分,**不得混用**;以官方示例为准。\n- **请求体要点**:`contents` 承载用户文本;`generationConfig` 内配置 **`responseModalities`**(如含 `IMAGE`)、**`imageConfig`**(如 **`aspectRatio`**、**`imageSize`** 等)。常见 **1K** 配图可在 `imageSize` 中使用 **`1K`**,具体以文档为准。\n- **响应**:从 `candidates[].content.parts[]` 中解析 **`inlineData`**(base64)与 `mimeType`,再写入二进制文件。\n\n## 模型与尺寸(摘要)\n\n| 类型 | 说明 |\n|------|------|\n| `gemini-3-pro-image` | 宽高比多由 `imageConfig.aspectRatio` 表达;分辨率档见 `imageSize`(如 1K)。 |\n| `gemini-3.1-flash-image` | 同上(`generateContent`);名称以 `v1beta/models` 为准。 |\n| `doubao-seedream-4.5` | 尺寸常为具体像素串或文档给定别名;总像素区间以文档为准。 |\n\n**尺寸与别名** 以 UCloud 当前文档为准;本技能内示例脚本若含别名表,仅为方便使用,**上线前请对照文档校验**。\n\n## 本技能内参考脚本\n\n- **路径**:[`scripts/generate_ucloud_image.py`](scripts/generate_ucloud_image.py)(仅 UCloud ModelVerse:`images/generations` 与 `v1beta` `generateContent`)。\n- **依赖**:Python 3、`requests`。\n- **密钥**:`UCLOUD_API_KEY` 或项目根 `.config/UCLOUD_API_KEY`。\n\n```bash\npython path/to/image-generation-ucloud/scripts/generate_ucloud_image.py --list-models\npython path/to/image-generation-ucloud/scripts/generate_ucloud_image.py \\\n --prompt prompt.txt --output out.png --size 4:3\npython path/to/image-generation-ucloud/scripts/generate_ucloud_image.py \\\n --prompt prompt.txt --output out.png --model gemini-3.1-flash-image --size 16:9 --image-size 1K\n```\n\nOpenRouter 出图请使用 **`image-generation-openrouter`** 目录下脚本,勿与本脚本混用。\n\n## 与总技能、OpenRouter 的关系\n\n- 总览与工作流:**[image-generation](https://github.com/baklib-tools/skills/blob/main/skills/image-generation/SKILL.md)**。\n- 若 UCloud 不可用或需使用 OpenRouter 路由的 Gemini 图像能力:**[image-generation-openrouter](https://github.com/baklib-tools/skills/blob/main/skills/image-generation-openrouter/SKILL.md)**。\n\n## 不做什么\n\n- 不绑定某一私有仓库目录或内部文件名(如「某营销目录下的唯一规范」);公开读者应能在自己的项目中复现「密钥 + 端点 + 请求体」。\n"}}, "examples/requirements-to-published-content-health-cms/artifacts/image-plan-from-content.md": {"f0df7b92f1a53c54a6bfb7cd8320cc2fc03db76c": {"t": 1785596116.2797935, "n": 1597, "h": "1e5a31267a4703e3", "c": "# 配图推导说明(F 阶段示例):由文案到三镜头\n\n> **原则**:用户**不**逐张指定「画什么、用什么色」;模型在加载 **image-generation** 与 **nano-banana-pro-prompting** 等技能后,根据已定稿的 [05-wechat-article.md](05-wechat-article.md) / [04-public-article.md](04-public-article.md) **自行推导**镜头叙事与提示词要点。\n\n---\n\n## 1. 叙事抽取\n\n| 文稿段落 | 核心信息 | 情绪 / 视觉隐喻 |\n|----------|----------|-----------------|\n| 导语 +「散、乱、难追溯」 | 多端分散、版本不一 | **压迫感、碎片化** |\n| 「一个库」「统一存储」 | 收敛、结构化 | **由乱到治、秩序感** |\n| 「分期」「同源发布」「路线图」 | 渐进、可落地 | **由暗到亮、路径清晰** |\n\n---\n\n## 2. 三镜头结构(与公众号运营常见节奏一致)\n\n| 镜头 | 用途 | 推导依据 |\n|------|------|----------|\n| **Shot 1 · 痛点** | 篇首图 | 导语中「网盘、旧后台、公众号各一版」→ 多终端混乱、文档堆叠意象 |\n| **Shot 2 · 方案** | 中段 | 「知识库 + 资源库」「角色与审核」→ 仪表盘式整洁界面或抽象「单一枢纽」 |\n| **Shot 3 · 结果 / CTA** | 篇尾 | 「分期路线图」「同源发布」→ 向上台阶、路径展开或团队协作明亮场景 |\n\n---\n\n## 3. 提示词要点(示例级,非最终出图指令)\n\n模型在撰写**分步英文/中文提示词**时,可参考 **nano-banana-pro-prompting** 的 Step 1–9 结构;以下为**要点级**摘要:\n\n- **Shot 1**:医疗内容运营向场景,多屏幕/多文件夹隐喻分散;冷色或对比强调混乱;避免具体真实产品界面与可辨识商标。 \n- **Shot 2**:抽象「统一内容枢纽」:单一路径汇聚文档与媒体;青绿色系(与品牌示例一致时可弱化为中性医疗蓝绿);扁平插画或等距视角。 \n- **Shot 3**:三阶段阶梯或时间轴「统一 → 治理 → 集成」;暖色高光、留白充足;可加一句**图内短标题**(须与 Step 7 文案逐字一致,若采用文内字)。\n\n**自检**:图内中文是否正确、是否误画 HEX/口令;系列三图是否在**同一条提示词内自洽**(无「与上一张一致」类跨请求依赖)。\n\n---\n\n## 4. 与技能链的关系\n\n```text\n05-wechat-article.md(定稿)\n ↓\n本文件(推导镜头与约束)\n ↓\nimage-generation / nano-banana-pro-prompting(生成提示词与可选脚本出图)\n ↓\nwechat-mp-html(将成图插入 HTML 的 img src,再发布)\n```\n\n本示例已在仓库内生成 **三帧 PNG**(`artifacts/images/rtc-demo-shot1.png` 等),由 **`image-generation-ucloud`** 脚本调用 UCloud Gemini 图像模型产出;提示词见 `artifacts/images/prompts/`。重新生成或更换模型时请在仓库根目录执行(需配置 `.config/UCLOUD_API_KEY`),详见 [artifacts/images/README.md](images/README.md)。\n"}}}} |