1 line
25 KiB
JSON
1 line
25 KiB
JSON
{"repo": "chenoneto/sensenova-infographic-skill", "files": {"scripts/generate_infographic.py": {"19c711520a22c2531c58b802db474d38a0335c9d": {"t": 1785768242.805694, "n": 11446, "h": "f681c85c77745bce", "c": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\"\"\"\nSenseNova U1 Fast 信息图生成脚本(纯标准库,零第三方依赖,跨平台)。\n\n用途:调用商汤 sensenova-u1-fast 图像生成接口,把一段结构化中文\nPrompt 渲染成信息图,并把返回的签名 URL 下载保存到本地。\n\n接口:POST https://token.sensenova.cn/v1/images/generations (OpenAI images 兼容)\n鉴权:Authorization: Bearer $SENSENOVA_API_KEY\n\n示例:\n python generate_infographic.py \\\n --prompt-file prompt.txt \\\n --size 2752x1536 \\\n --output ./out/infographic.png\n\n # 按宽高比快速选尺寸(16:9 自动映射到 2752x1536)\n python generate_infographic.py --prompt-file prompt.txt --aspect-ratio 16:9\n\n # 干运行:只校验 Prompt,不烧 Token\n python generate_infographic.py --prompt-file prompt.txt --dry-run --show-prompt\n\n # JSON 输出(便于自动化流程解析)\n python generate_infographic.py --prompt-file prompt.txt --json --output ./out.png\n\"\"\"\nimport argparse\nimport json\nimport os\nimport re\nimport sys\nimport time\nimport urllib.error\nimport urllib.request\n\n# Windows GBK 控制台下强制 UTF-8 输出,避免中文/emoji 报错\ntry:\n sys.stdout.reconfigure(encoding=\"utf-8\")\n sys.stderr.reconfigure(encoding=\"utf-8\")\nexcept Exception:\n pass\n\nBASE_URL = \"https://token.sensenova.cn/v1\"\nENDPOINT = BASE_URL + \"/images/generations\"\nMODEL = \"sensenova-u1-fast\"\n\n# 11 种官方白名单分辨率(传其它值会 400)\nVALID_SIZES = {\n \"2048x2048\", \"2752x1536\", \"1536x2752\", \"2496x1664\", \"1664x2496\",\n \"2368x1760\", \"1760x2368\", \"2272x1824\", \"1824x2272\", \"3072x1376\",\n \"1344x3136\",\n}\nDEFAULT_SIZE = \"2752x1536\"\n\n# 常用宽高比 → 官方白名单分辨率(对外友好,对内精确)\nASPECT_RATIOS = {\n \"16:9\": \"2752x1536\",\n \"9:16\": \"1536x2752\",\n \"1:1\": \"2048x2048\",\n \"3:2\": \"2496x1664\",\n \"2:3\": \"1664x2496\",\n \"4:3\": \"2368x1760\",\n \"3:4\": \"1760x2368\",\n \"5:4\": \"2272x1824\",\n \"4:5\": \"1824x2272\",\n \"21:9\": \"3072x1376\",\n \"9:21\": \"1344x3136\",\n}\n\nMAX_RETRIES = 3\nRETRY_BACKOFF = [5, 15, 30] # 秒\n\n\ndef log(msg, force_stdout=False):\n \"\"\"默认输出到 stderr,方便 --json 模式把 stdout 留给机器解析。\"\"\"\n if force_stdout:\n print(msg, flush=True)\n else:\n print(msg, file=sys.stderr, flush=True)\n\n\ndef approx_tokens(prompt):\n \"\"\"粗略估算:1 token ≈ 1.5 中文字符。\"\"\"\n return int(len(prompt) / 1.5)\n\n\ndef lint_prompt(prompt):\n \"\"\"对 Prompt 做静态检查,返回警告列表。\"\"\"\n warnings = []\n # HEX 色值容易让 U1 当成可见文字渲染\n hex_codes = re.findall(r\"#[0-9A-Fa-f]{6}\\b\", prompt)\n if hex_codes:\n warnings.append(\n \"Prompt 中检测到 HEX 色值 %s,可能会被 U1 渲染为可见文字。\"\n \"建议只在设计描述里用颜色名称(如'青色霓虹'),不要写 HEX 代码。\" % hex_codes\n )\n # 过长的提示词\n tokens = approx_tokens(prompt)\n if tokens > 4096:\n warnings.append(\"Prompt 约 %d tokens,超过 4096 上限,建议精简或拆图。\" % tokens)\n elif tokens > 3000:\n warnings.append(\"Prompt 约 %d tokens,接近上限,建议检查是否冗余。\" % tokens)\n # 缺少关键结构标记\n for marker in [\"【整体风格】\", \"【整体布局】\", \"【标题区】\"]:\n if marker not in prompt:\n warnings.append(\"未检测到 %s,结构化程度不足可能导致 U1 跑偏。\" % marker)\n # 约束词不足\n if \"溢出\" not in prompt and \"安全间距\" not in prompt:\n warnings.append(\"建议增加'元素与边框保持安全间距,严禁溢出'等约束,减少排版翻车。\")\n return warnings\n\n\ndef build_request(prompt, size, n):\n payload = {\n \"model\": MODEL,\n \"prompt\": prompt,\n \"size\": size,\n \"n\": n,\n }\n return json.dumps(payload, ensure_ascii=False).encode(\"utf-8\")\n\n\ndef call_api(api_key, body, timeout):\n req = urllib.request.Request(\n ENDPOINT,\n data=body,\n method=\"POST\",\n headers={\n \"Authorization\": \"Bearer \" + api_key,\n \"Content-Type\": \"application/json\",\n },\n )\n with urllib.request.urlopen(req, timeout=timeout) as resp:\n return json.loads(resp.read().decode(\"utf-8\"))\n\n\ndef download(url, dest, timeout):\n req = urllib.request.Request(url, headers={\"User-Agent\": \"sensenova-infographic/1.1\"})\n with urllib.request.urlopen(req, timeout=timeout) as resp:\n data = resp.read()\n os.makedirs(os.path.dirname(os.path.abspath(dest)), exist_ok=True)\n with open(dest, \"wb\") as f:\n f.write(data)\n return len(data)\n\n\ndef parse_error_body(e):\n try:\n return e.read().decode(\"utf-8\", errors=\"replace\")\n except Exception:\n return \"\"\n\n\ndef output_json(result, exit_code=0):\n \"\"\"输出 JSON 并退出。\"\"\"\n result[\"exit_code\"] = exit_code\n print(json.dumps(result, ensure_ascii=False), flush=True)\n sys.exit(exit_code)\n\n\ndef main():\n ap = argparse.ArgumentParser(description=\"Generate infographic via SenseNova U1 Fast\")\n g = ap.add_mutually_exclusive_group(required=True)\n g.add_argument(\"--prompt\", help=\"信息图 Prompt 文本(结构化中文)\")\n g.add_argument(\"--prompt-file\", help=\"包含 Prompt 的文本文件路径(UTF-8)\")\n ap.add_argument(\"--size\", default=DEFAULT_SIZE, help=\"分辨率,默认 %s\" % DEFAULT_SIZE)\n ap.add_argument(\"--aspect-ratio\", help=\"按宽高比选尺寸(16:9, 9:16, 1:1, 4:5 ...),自动映射到白名单\")\n ap.add_argument(\"--n\", type=int, default=1, help=\"生成张数,默认 1\")\n ap.add_argument(\"--output\", default=\"./infographic.png\", help=\"输出文件路径(多张时自动加序号)\")\n ap.add_argument(\"--api-key\", default=os.environ.get(\"SENSENOVA_API_KEY\", \"\"),\n help=\"API Key,默认读环境变量 SENSENOVA_API_KEY\")\n ap.add_argument(\"--timeout\", type=int, default=180, help=\"超时秒数,默认 180\")\n ap.add_argument(\"--dry-run\", action=\"store_true\", help=\"不调用 API,只校验并打印请求体\")\n ap.add_argument(\"--show-prompt\", action=\"store_true\", help=\"打印最终使用的 Prompt\")\n ap.add_argument(\"--json\", action=\"store_true\", help=\"最终输出 JSON 格式,便于脚本解析\")\n args = ap.parse_args()\n\n # 尺寸解析:--aspect-ratio 优先级高于 --size\n if args.aspect_ratio:\n ar = args.aspect_ratio.strip()\n if ar not in ASPECT_RATIOS:\n log(\"[错误] 不支持的宽高比 '%s'。仅支持:%s\" % (ar, \", \".join(sorted(ASPECT_RATIOS.keys()))))\n if args.json:\n output_json({\"status\": \"error\", \"message\": \"unsupported aspect ratio\", \"aspect_ratio\": ar}, 2)\n return 2\n args.size = ASPECT_RATIOS[ar]\n log(\"[信息] 宽高比 %s 已映射为分辨率 %s\" % (ar, args.size))\n\n if args.size not in VALID_SIZES:\n log(\"[错误] 非法分辨率 '%s'。仅支持以下 11 种:\" % args.size)\n log(\" \" + \", \".join(sorted(VALID_SIZES)))\n if args.json:\n output_json({\"status\": \"error\", \"message\": \"invalid size\", \"size\": args.size}, 2)\n return 2\n\n # 读取 Prompt\n if args.prompt_file:\n try:\n with open(args.prompt_file, \"r\", encoding=\"utf-8\") as f:\n prompt = f.read().strip()\n except OSError as e:\n log(\"[错误] 读取 prompt 文件失败:%s\" % e)\n if args.json:\n output_json({\"status\": \"error\", \"message\": \"failed to read prompt file\", \"error\": str(e)}, 2)\n return 2\n else:\n prompt = args.prompt.strip()\n\n if not prompt:\n log(\"[错误] Prompt 为空。\")\n return 2\n\n tokens = approx_tokens(prompt)\n if args.show_prompt:\n log(\"=\" * 60)\n log(\"[最终 Prompt](约 %d tokens):\" % tokens)\n log(prompt)\n log(\"=\" * 60)\n\n # 静态检查\n for w in lint_prompt(prompt):\n log(\"[提示] %s\" % w)\n\n body = build_request(prompt, args.size, args.n)\n\n if args.dry_run:\n log(\"[信息] 干运行模式,不调用 API,已校验通过。\")\n log(\"[信息] 请求体:\")\n log(body.decode(\"utf-8\"))\n if args.json:\n output_json({\n \"status\": \"dry_run\",\n \"model\": MODEL,\n \"size\": args.size,\n \"n\": args.n,\n \"tokens\": tokens,\n \"prompt\": prompt,\n }, 0)\n return 0\n\n # 前置检查:API Key\n if not args.api_key:\n log(\"[错误] 未提供 API Key。请设置环境变量 SENSENOVA_API_KEY,或用 --api-key 传入。\")\n log(\" 获取地址:https://platform.sensenova.cn/console → API Keys(sk- 开头)\")\n if args.json:\n output_json({\"status\": \"error\", \"message\": \"missing API key\"}, 2)\n return 2\n\n log(\"[信息] 模型 %s | 尺寸 %s | 张数 %d | Prompt 约 %d tokens\"\n % (MODEL, args.size, args.n, tokens))\n\n # 带重试的 API 调用\n result = None\n error_detail = None\n for attempt in range(1, MAX_RETRIES + 1):\n try:\n log(\"[信息] 正在请求生成(第 %d/%d 次尝试)...\" % (attempt, MAX_RETRIES))\n result = call_api(args.api_key, body, args.timeout)\n break\n except urllib.error.HTTPError as e:\n detail = parse_error_body(e)\n error_detail = detail\n log(\"[错误] HTTP %s:%s\" % (e.code, detail[:500]))\n if e.code in (401, 403):\n log(\" → API Key 无效/无权限,请检查后重试,不再自动重试。\")\n if args.json:\n output_json({\"status\": \"error\", \"http_code\": e.code, \"message\": \"invalid API key\", \"detail\": detail}, 1)\n return 1\n if e.code == 400:\n log(\" → 请求参数错误,请检查 size 是否为白名单、prompt 是否合法。\")\n if args.json:\n output_json({\"status\": \"error\", \"http_code\": e.code, \"message\": \"bad request\", \"detail\": detail}, 1)\n return 1\n if e.code in (429, 500, 502, 503, 504) and attempt < MAX_RETRIES:\n wait = RETRY_BACKOFF[attempt - 1]\n log(\" → 可重试错误,%d 秒后重试。\" % wait)\n time.sleep(wait)\n continue\n if args.json:\n output_json({\"status\": \"error\", \"http_code\": e.code, \"message\": \"http error\", \"detail\": detail}, 1)\n return 1\n except (urllib.error.URLError, TimeoutError) as e:\n error_detail = str(e)\n log(\"[错误] 网络/超时:%s\" % e)\n if attempt < MAX_RETRIES:\n wait = RETRY_BACKOFF[attempt - 1]\n log(\" → %d 秒后重试。\" % wait)\n time.sleep(wait)\n continue\n if args.json:\n output_json({\"status\": \"error\", \"message\": \"network/timeout\", \"detail\": str(e)}, 1)\n return 1\n\n if not result:\n log(\"[错误] 多次尝试后仍未获得结果。\")\n if args.json:\n output_json({\"status\": \"error\", \"message\": \"no result after retries\"}, 1)\n return 1\n\n data = result.get(\"data\") or []\n if not data:\n log(\"[错误] 返回结果无 data 字段:%s\" % json.dumps(result, ensure_ascii=False)[:500])\n if args.json:\n output_json({\"status\": \"error\", \"message\": \"no data field in response\", \"response\": result}, 1)\n return 1\n\n # 下载保存\n base, ext = os.path.splitext(args.output)\n if not ext:\n ext = \".png\"\n saved = []\n for i, item in enumerate(data):\n url = item.get(\"url\")\n if not url:\n log(\"[警告] 第 %d 张无 url,跳过。\" % (i + 1))\n continue\n dest = args.output if len(data) == 1 else \"%s_%d%s\" % (base, i + 1, ext)\n try:\n size_bytes = download(url, dest, args.timeout)\n saved.append({\"path\": dest, \"bytes\": size_bytes, \"url\": url})\n log(\"[成功] 已保存:%s(%.1f KB)\" % (dest, size_bytes / 1024.0))\n except Exception as e:\n log(\"[警告] 下载第 %d 张失败:%s\" % (i + 1, e))\n log(\" 原始 URL(1 小时内有效):%s\" % url)\n\n if not saved:\n log(\"[错误] 没有任何图片保存成功。\")\n if args.json:\n output_json({\"status\": \"error\", \"message\": \"no images saved\"}, 1)\n return 1\n\n log(\"[完成] 共保存 %d 张信息图。\" % len(saved))\n\n if args.json:\n output_json({\n \"status\": \"success\",\n \"model\": MODEL,\n \"size\": args.size,\n \"n_requested\": args.n,\n \"n_saved\": len(saved),\n \"tokens\": tokens,\n \"saved\": saved,\n }, 0)\n else:\n # 机器可读输出,方便上层解析\n print(\"SAVED_FILES=\" + json.dumps([s[\"path\"] for s in saved], ensure_ascii=False))\n\n return 0\n\n\nif __name__ == \"__main__\":\n sys.exit(main())\n"}}, "SKILL.md": {"19c711520a22c2531c58b802db474d38a0335c9d": {"t": 1785768263.426032, "n": 6343, "h": "128b46b7fa57de82", "c": "---\nname: sensenova-infographic\ndescription: >\n 用商汤 SenseNova U1 Fast(sensenova-u1-fast)生成中文信息图 / 海报 / 知识卡片。\n 该模型是专精信息图的原生图像生成模型,文字渲染达商业级精度、排版规整。\n 内置针对中文场景优化的结构化提示词模板库(对比图/流程图/数据图/知识卡片/清单/海报)。\n Use when the user wants to make an infographic, poster, knowledge card, data\n visualization image, or 信息图 with SenseNova / 商汤 / U1.\n 触发词:信息图, 生成信息图, 商汤信息图, SenseNova, sensenova-u1-fast, U1, 海报,\n 知识卡片, 数据可视化图, infographic, 科普图, 流程图海报, 清单图。\nagent_created: true\n---\n\n# SenseNova U1 Fast — 中文信息图生成\n\n## Overview\n\n`sensenova-u1-fast` 是商汤 SenseNova U1(NEO-Unify 理解生成统一架构)的加速版,**专精信息图**:\n文字渲染达商业级精度、能精准控制多栏边界、排版规整。本 skill 把「用户内容 → 结构化中文\nPrompt → 调 API 生图 → 下载保存」串成稳定流程,并内置一套**中文优化提示词模板库**。\n\n- 接口:`POST https://token.sensenova.cn/v1/images/generations`(OpenAI images 兼容)\n- 模型:`sensenova-u1-fast`(固定)| Prompt 上限 **4096 tokens** | 11 种固定分辨率\n- 生图脚本:`scripts/generate_infographic.py`(纯标准库,零依赖,自动下载+重试)\n- 提示词模板:`references/prompt-guide.md` | 接口细节:`references/api.md`\n\n> ⚠️ **U1 Fast 不是万能模型**:它只擅长**信息图/海报/排版类图像生成**。不要用它做:\n> - 通用写实照片(人物/风景)\n> - 图像理解/识图/问答\n> - 纯艺术创作(它会被强制结构化,反而受约束)\n> 对话/识图请走 `/v1/chat/completions` + `sensenova-6.7-flash-lite`。\n\n\n---\n\n## Prerequisites\n\n🔴 **CHECKPOINT — 生图前必须确认 API Key,否则调用必失败:**\n\n1. 访问 https://platform.sensenova.cn/console → API Keys,创建 sk- 开头的密钥(公测免费)。\n2. 设置环境变量(**每次调用脚本都必须带**,Windows 还要带 UTF-8):\n - Windows(bash):`export SENSENOVA_API_KEY=sk-xxx`\n - 或调用脚本时用 `--api-key sk-xxx` 传入\n3. **确认 Key 就绪后再进入下面的流程。** 未就绪 → 停下,先引导用户去控制台创建。\n\n⚠️ API Key 是敏感凭据:**绝不硬编码进代码或提交到仓库**,只走环境变量 / `--api-key`。\n\n---\n\n## Workflow(5 步,逐步执行)\n\n### Step 1 · 明确需求(输入 → 输出)\n问清并锁定 4 件事,缺则用合理默认并**明确告知用户**:\n- **主题内容**:要表达什么?把关键文字/数据尽量拿全(U1 渲染的就是你给的字)。\n- **信息图类型**:对比 / 流程 / 数据 / 知识卡片 / 清单 / 海报(决定套哪个模板)。\n- **风格**:扁平商务 / 可爱卡通 / 国风 / 科技 / 手账 / 波普 / 数据仪表(见 prompt-guide 风格库)。\n- **尺寸**:默认横版 `2752x1536`;竖屏 `1536x2752`;小红书 `1824x2272`(见 api.md 尺寸表)。\n\n### Step 2 · 选模板(读 prompt-guide.md)\n按类型从 `references/prompt-guide.md` 的模板库选一个骨架(A 对比 / B 流程 / C 数据 / D 知识卡片 / E 清单 / F 海报)。**必须实际读取该文件**,不要凭记忆。\n\n### Step 3 · 组装结构化中文 Prompt(核心,决定成败)\n按黄金 5 段填满模板:【整体风格】【配色方案】【整体布局】【标题区】【各区块内容+底部】。\n遵守五条铁律:① 文字写原文 ② 图标描述造型 ③ 布局用几何词 ④ 配色先定死 ⑤ 加防溢出约束。\nPrompt 尽量写详细(上限 4096 tokens,用满更稳)。把最终 Prompt 存成 UTF-8 文件,例如\n`<工作目录>/_sensenova_prompt.txt`,避免命令行超长/转义问题。\n\n🔴 **CHECKPOINT**:把组装好的 Prompt 先展示给用户确认(尤其文字/数据/配色),确认后再生图。\n\n### Step 4 · 调用脚本生图\n\n```bash\n# 基础调用\nPYTHONIOENCODING=utf-8 python \"C:/Users/MSI/.workbuddy/skills/sensenova-infographic/scripts/generate_infographic.py\" \\\n --prompt-file \"<工作目录>/_sensenova_prompt.txt\" \\\n --size 2752x1536 \\\n --output \"<工作目录>/信息图_主题.png\"\n\n# 按宽高比快速选尺寸(16:9 / 9:16 / 1:1 / 4:5 ...)\nPYTHONIOENCODING=utf-8 python scripts/generate_infographic.py \\\n --prompt-file prompt.txt --aspect-ratio 16:9 --output out.png\n\n# 干运行:不烧 Token,先校验 Prompt / 请求体\nPYTHONIOENCODING=utf-8 python scripts/generate_infographic.py \\\n --prompt-file prompt.txt --dry-run --show-prompt\n\n# JSON 输出,方便自动化流程解析\nPYTHONIOENCODING=utf-8 python scripts/generate_infographic.py \\\n --prompt-file prompt.txt --json --output out.png\n```\n\n- 脚本自动:校验 size 白名单 → Prompt 静态检查(HEX 色值、token 超限、结构缺失) → 调 API(401/400 不重试,429/5xx/超时退避重试 3 次) → 下载签名 URL 保存。\n- 多张时(`--n>1`)自动加序号。成功后:默认输出 `SAVED_FILES=[...]`;`--json` 模式输出完整 JSON 结果。\n\n\n### Step 5 · 展示结果\n用 present_files 展示保存的 PNG。若用户要微调,回 Step 3 改 Prompt 局部再生成(不要从头重写)。\n\n---\n\n## 内置提示词模板速查(详见 references/prompt-guide.md)\n\n| 模板 | 用途 | 默认尺寸 |\n|------|------|---------|\n| A 三栏对比/并列要点 | 优缺点、方案对比、三大功能 | 2752x1536 |\n| B 流程步骤图 | 操作步骤、工作流、时间线 | 2752x1536 / 竖版 |\n| C 数据可视化 | 报告、统计、KPI 大数字 | 2752x1536 / 2496x1664 |\n| D 知识卡片/九宫格 | 科普、教程、小红书 | 1536x2752 / 2048x2048 |\n| E 清单 Checklist | 待办、要点、攻略 | 1536x2752 / 1344x3136 |\n| F 单页海报/封面 | 活动、公告、封面 | 1664x2496 / 1536x2752 |\n\n**黄金结构**(任何图都套):【整体风格】+【配色方案】+【整体布局】+【标题区】+【各区块精确文字与图标】+【底部】。\n\n---\n\n## 失败模式与回退(if-then)\n\n### 阶段一 · 生图前检查\n| 检查项 | 失败 → 处理 |\n|--------|-----------|\n| API Key 是否就绪 | → **立即停止**,引导去 https://platform.sensenova.cn/console 创建 |\n| size 是否在 11 白名单内 | → 脚本直接报错并列出白名单;换合法值;或用 `--aspect-ratio` 映射 |\n| Prompt 是否 ≤4096 tokens | → 脚本打印约算 token 警告;精简文字或拆两张图 |\n| Prompt 是否结构化 | → 若是\"意识流\"一句话,先按黄金 5 段重写再生图 |\n| 脚本静态检查是否有警告(HEX色值/缺结构/缺约束) | → 按提示修复后再生图;可用 `--dry-run` 先不烧 Token 校验 |\n\n### 阶段二 · API 调用失败\n| 错误 | 含义 | 一线修复 | 仍失败 → 兜底 |\n|------|------|---------|-------------|\n| **401/403** | Key 无效/无权限 | 去控制台确认 Key 有效、未注销 | 让用户重建 Key;不自动重试 |\n| **400** | 参数错误 | 检查 model=`sensenova-u1-fast`、size 白名单 | 回退最小参数(model+prompt+默认 size)重试 |\n| **404** | 接口用错 | 确认走 `/v1/images/generations` 而非 chat | 核对 Base URL 子域名是 `token.sensenova.cn` |\n| **429** | 频率超限(1500次/5h) | 等待 Retry-After | 退避 5→15→30s 重试,最多 3 次 |\n| **5xx/超时** | 服务端/网络 | 退避重试 | 3 次失败告知用户稍后再试 |\n| **URL 下载失败** | 签名 URL 过期(>1h) | 立即重跑生图拿新 URL | 脚本会打印原始 URL 供手动下载 |\n\n### 阶段三 · 出图质量不达标(实测高频)\n| 症状 | 修复(改 Prompt 后重生) |\n|------|------------------------|\n| 文字模糊/错字 | 加「标题粗体、小字边缘锐利」+ 简化背景;文字过多则拆图 |\n| 布局混乱 | 换几何布局词 + 强调\"区块外观完全统一\";区块数降到 ≤4 |\n| 内容溢出边框 | 加「元素与边框保持安全间距,严禁溢出」;缩短单块文字 |\n| 配色杂乱 | 先定死主/辅/背景/文字四色;必要时降到双色 |\n\n---\n\n## 反例黑名单(不要做的事)\n\n| # | 禁止行为 | 为什么 | 正确做法 |\n|---|---------|--------|---------|\n| 1 | **硬编码 API Key 到代码/仓库** | 泄露凭据 | 只用 `$SENSENOVA_API_KEY` 或 `--api-key` |\n| 2 | **写\"意识流\" Prompt**(如\"画个好看的海报字大点\")| U1 会跑偏、抽卡 | 按黄金 5 段结构化,文字写原文 |\n| 3 | **只说\"配几个图标\"不描述造型** | 图标随机、不符主题 | 明确图标位置/造型/颜色 |\n| 4 | **传非白名单分辨率**(如 `1920x1080`)| 直接 400 | 只用 11 种官方尺寸 |\n| 5 | **用生图接口去做问答/识图** | 404 | 对话/识图走 `/v1/chat/completions` + `sensenova-6.7-flash-lite` |\n| 6 | **Prompt 里只写\"介绍三点\"不给原文** | 模型自己编字,不可控 | 把三点的完整文字逐字写进 Prompt |\n| 7 | **一次塞太多区块(>4-5块)+ 海量文字** | 排版必乱、文字糊 | 精简或拆成多张图 |\n| 8 | **Windows 下不设 UTF-8 就跑脚本** | 中文/emoji 可能 GBK 报错 | 命令前加 `PYTHONIOENCODING=utf-8` |\n| 9 | **签名 URL 拿到手不及时下载** | URL 约 1 小时失效 | 立即下载保存(脚本已自动做) |\n\n---\n\n## Runtime 适配性声明\n\n本 skill 遵循 Agent Skills Standard,可在 Claude Code、Codex、Cursor、WorkBuddy、Hermes、\nOpenClaw、Gemini CLI 等任何 skills-compatible runtime 运行。生图通过标准 HTTP(Python 标准库)\n执行,无 runtime 特定依赖;脚本路径按各 runtime 的 skills 目录自行替换即可。\n\n---\n\n## 资源速查\n\n| 路径 | 用途 |\n|------|------|\n| `scripts/generate_infographic.py` | 生图脚本:校验/调用/重试/下载,零依赖 |\n| `references/prompt-guide.md` | 中文提示词工程指南 + 6 套模板库 + 风格库 |\n| `references/api.md` | 接口规格 + 11 种分辨率 + 错误码 |\n\n## 版本记录\n\n| 版本 | 日期 | 变更 |\n|------|------|------|\n| 1.1 | 2026-07-20 | 脚本增强:支持 `--aspect-ratio`、`--dry-run`、`--show-prompt`、`--json`;新增 Prompt 静态检查(HEX色值/token/结构/约束);prompt-guide 补充配色写法指南与完整示例;SKILL.md 补充 U1 不适用场景 |\n| 1.0 | 2026-07-17 | 初始版本:U1 Fast 信息图生成 + 中文优化提示词模板库 + 失败模式/黑名单/检查点 |\n"}}}} |