feat(monitor): 抖音 Web 接口客户端 —— 绕开爬虫子进程,直接发 HTTP
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s

移植自 mac-agent-os 的 mediacrawler_adapter:不起子进程、不开页面,用浏览器里那份
登录态直接调抖音 Web 接口。产物键名照抄 store/douyin,所以 ingest 那条链路一个字不用改。

**目前能用的(真环境实测,非推断)**:

    profile/other : 200, 7075 字节  —— 博主主页指标(粉丝/获赞/作品数/昵称)
    aweme/detail  : 200, 45425 字节 —— 单条作品详情(含点赞/评论/收藏/分享)

**目前不能用的:作品列表 `aweme/post`。** 两个互相独立的原因:
  1. 这个接口被抖音单独升级成了真校验:不带 x-tt-argus 回 403「Uifid Not Found」,
     带上 dummy 值回 200 + **空 body**。也就是说「头在不在」骗得过,「真校验」过不了。
     同一套头打 profile/other 和 aweme/detail 都是通的 —— 抖音是挑着接口加保护的,
     挑中的恰好是「批量拉作品列表」这个最敏感的动作。
  2. 改走页面截获也不行:CDP 浏览器打开博主主页会落到「验证码中间页」(当天大量探测的
     代价,过几小时要重测)。

  所以现在的边界是:**已知作品的指标刷新能做,自动发现新作品做不了**。

**排查中控住变量后得到的两条事实**(都写进注释了):
  · `Accept` / `Accept-Language` / `Referer` 才是主页接口能返回真数据的原因 —— 只有
    UA+client hints+Cookie 时是 200 但仅 121 字节的空壳,补上这三个头变 7074 字节。
    (我先前猜的 sec-ch-ua 不是关键。)
  · 因此 UA 与 client hints 必须**成套地取自同一个浏览器**,所以 BrowserIdentity 一次
    从 CDP 取齐 cookie + UA + hints,而不是各自写死。

「200 + 空 body 必须当场报错」也是刻意写死的:放过去它会在下游变成「这个博主没作品」,
把一次失败伪装成一条正常结果 —— 爬虫那条路正是这么栽的,还被翻译成「账号被封」。

测试 +11:cookie 解析、请求头成套性(含 uifid 缺失/回退)、产物键名与 store 对齐、
以及 _get 的三条失败路径(空 body / 403 带网关原话 / 正常返回)。
This commit is contained in:
2026-10-10 17:12:25 +08:00
parent 21ff01b894
commit 2112c1a870
1601 changed files with 367425 additions and 0 deletions
@@ -0,0 +1,245 @@
---
name: character-refs
description: |
给任何故事里的角色真出参考图(小说改编、自己原创的故事、单独设计一个角色都行,不需要小说原文):
一段话描述角色,拆成分层字段、补全后确认,
先出一张正面全身锚点,其余视图(大头照、90° 侧面、背面、细节、45° 大头照)都只参考这张锚点,
按需分档出图。每张图带标识、可单独重出,重出后自动标出哪些图过期。
支持 Qwen Image(ComfyUI)、codex 内置出图、OpenAI Images API(GPT Image 2)和自定义命令,主图与派生图可以使用不同模型。
产出 asset.json + 一组 PNG + 双击就能开的报告。零依赖。
只要提示词、不出图的话使用 novel-characters。
Use when asked to 出角色图、做角色参考图、给我的角色出图、定妆照、真出三视图、character reference images。
allowed-tools:
- Read
- Write
- Bash
metadata:
version: 2.1.0
triggers:
- character-refs
- 角色参考图
- 角色图
- 出角色图
- 创建角色
- 定妆照
- 三视图
- character reference images
license: Apache-2.0
requires:
bins:
- node # >= 18,只用标准库,无 npm 依赖
runtimes:
- claude-code
- codex
---
## character-refs
输入一段角色描述,输出一组角色参考图——**角色从哪来都行**:小说改编、自己编的故事、临时想到的一个人。**一张正面全身锚点,其余每张都只参考这张锚点**。
给视频模型(H3 / Wan / Seedance)当参考图使用,也给人看。
`{baseDir}` = 本文件所在目录。脚本 `{baseDir}/scripts/character-refs.mjs`,零依赖,`node` 直接跑。
**这是仓库里唯一真出图的 skill。**其余 skill 只交提示词,因为出图那一刻才能决定模型和画风;
这个 skill 就是「那一刻」——模型在 Step 0 由用户选定,画风在建角色时选择(默认动漫,另有写实预设,也能挂自定义画风)。
### 核心规则(先读懂再动手)
- **一组图只有一个根:正面全身锚点。**只有它是文生图,其余全是「锚点 + 一句改图指令」。
头发、脸、领口三类细节额外挂确认过的正脸大头照。图与图之间不再互相参考——
所以任何一张出坏了,单独重出这一张就行,不会连锁。
- **分档,默认出到第二档**:
| 档位 | 内容 | 什么时候出 |
| --- | --- | --- |
| 1 | 正面全身(锚点) | 总是,第一张 |
| 2(默认) | 正脸大头照、90° 侧面(面朝画面右侧)、背面 | 默认就出齐 |
| 3 | 细节图(最多 8 张):用户点名的、角色最能认出来的地方;没有就用默认的头发 / 发饰、领口、袖口、鞋 | 有配饰或服装局部的特写 |
| 4(默认不出) | 45° 大头照(面朝画面左侧) | 用户明确要 |
H3 实测:只给正面全身,服装和背影都对,但脸会走样;**加一张大头照,脸最接近**。所以默认出齐第二档。
只要锚点(比如先看看长相再说):`gen <asset.json> --tier 1`。
- **模型不锁定**:主图与派生图可以使用不同模型,重出单张也可以换模型。一致性来自「同一张锚点」,
不来自「同一个模型」(实测 Qwen 锚点 + GPT 派生、GPT 锚点 + Qwen 派生、同组混用都衔接得上)。
每张图记录自己的模型,同组混用只在报告里提醒。
- **冻结的只有三样**:锚点文件(sha256)、画风快照、文字描述指纹。任何一样变了,
依赖它的图标成「过期」——**只标记,不删除**,旧版本全部留着。
---
### Step 0 — 初次设置(每台机器一次)
```bash
node {baseDir}/scripts/character-refs.mjs config --show
```
输出末尾写「还缺:……」就要问用户这三件事,**一次问完**:
1. **主图(锚点)使用哪个模型**:
- `qwen` —— Qwen Image 2.1,走自己的 ComfyUI 服务器。快(每张 25–45 秒)、尺寸精确、不吃订阅额度。
需要 `COMFY_URL`(可选 `COMFY_USER` / `COMFY_PASS`),放环境变量或一个 .env 文件
- `codex` —— codex 内置出图(GPT)。不要 API key,但**吃 ChatGPT 订阅额度**(Plus 约 25 张会触顶,几小时后恢复),每张 80–160 秒
- `openai` —— OpenAI Images API,默认 `gpt-image-2`。按量计费,要 `OPENAI_API_KEY`;也能指到兼容接口(`--openai-base-url`)
- `custom:<名字>` —— 用户自己的出图命令,见 `references/models.md`
2. **派生图使用哪个模型**:默认与主图相同。可以分开(比如锚点 GPT、派生 Qwen)
3. **出完锚点要不要停下来等人确认**:推荐 `yes`——锚点是全组的根,它不对后面全白出。
选 `no` 则自动放行,报告里标「未经人工确认」;**没过检查门的锚点不会自动放行**,要人看过再 `confirm` 或重出
```bash
node {baseDir}/scripts/character-refs.mjs config --model qwen --confirm-anchor yes --qwen-env-file <.env 路径>
# 分开设置:--anchor-model codex --derive-model qwen
```
配置写在 `{baseDir}/config.local.json`(不进版本控制,重新安装不会覆盖)。以后默认一直使用它,
用户说「换成 xx」就重新跑 `config`。**不要把 .env 里的密码或 key 打印出来**,`config --show` 已经打码。
### Step 1 — 一次性输入
用户**一段话**描述角色就够了,不要一项一项问。你(模型)来拆:
```bash
node {baseDir}/scripts/character-refs.mjs intake-template
```
照模板把描述拆进各字段,写到 `<工作目录>/<角色名>-intake.json`。规则(详见 `references/intake.md`):
- `lang` 填用户说话的语言(中文 `zh`、英文 `en`、日文 `ja` 内置)。确认表和报告都用这个语言;
其他语言先运行 `ui-template <lang>`,把打印出的英文文案逐项翻译,整块放进 intake 的 `ui` 字段
- 用户说了的标 `stated`,你推出来的标 `inferred`,按惯例补的标 `default`——**如实标**,确认表靠它提醒用户看哪几项
- **年龄、性别、年代推不出来就问用户**,只有这三样不许自己编;其余一律自己补,不要追问
- `en` 进提示词:**永远英文**、不写角色名、不写画风词;`text` 给人看,用 `lang` 的语言写(旧输入写的 `zh` 照样认)
- 服装分 `top` / `bottom`——大头照只用上装,带上下装模型就会把镜头拉远
- **细节图按角色挑**:用户点名的优先,没点名挑最能认出这个角色的地方,默认四槽位兜底;脸上的特征不出细节图(大头照已经看得清)。详见 `references/intake.md`
```bash
node {baseDir}/scripts/character-refs.mjs intake-check <intake.json>
```
有问题按报错改到通过。通过后它打印**确认表**,原样给用户看,推断和默认的项带标记。
**用户确认(或改完再确认)之后才往下走。**
### Step 2 — 建资产
```bash
node {baseDir}/scripts/character-refs.mjs new <intake.json> --out <输出目录> [--look 画风]
```
建出 `<输出目录>/<角色名>/asset.json`。画风层此时整份快照进资产,之后改预设不影响已有角色。
**画风**:用户说了就按用户说的选择,没说就是动漫。同一部剧的角色**全部使用同一个画风**。
| 预设 | `--look` 认的名字 |
| --- | --- |
| 动漫(默认) | `动漫` `卡通` `anime` `cartoon` `二次元` `アニメ` |
| 写实照片 | `写实` `realistic` `photo` `真人` |
`looks` 列出全部预设。用户要别的画风(国风、美漫……):`look-template <最接近的预设> > my-look.json`,改 `style`(介质、线条)、
`clean`(背景与光,**必须保留白底**)、`neg`(反向词——换成画出来的画风要把 `anime` `illustration` 从反向词里拿掉),
画出来的画风把 `medium` 写成 `drawn`,然后 `--look my-look.json`。自定义画风没有实测,先出锚点看效果。
已经建好的角色要换画风:
```bash
node {baseDir}/scripts/character-refs.mjs restyle <asset.json> --look 动漫
```
换完所有已有的图(连锚点)都标过期,旧图保留,从锚点开始重出。
### Step 3 — 出默认的第二档
```bash
node {baseDir}/scripts/character-refs.mjs gen <asset.json>
```
不给视图和档位,就是补齐第一、二档里还没出的、已过期的图。
配置了确认:这条命令**只出锚点就停下**。把图给用户看(报告或直接打开 PNG),用户说可以再
```bash
node {baseDir}/scripts/character-refs.mjs confirm <asset.json>
node {baseDir}/scripts/character-refs.mjs gen <asset.json> # 同一条命令,接着出大头照、侧面、背面
```
不满意就再跑一次 `gen <asset.json> front-full`(出 v2,可 `--seed` 换种子、`--model` 换模型),
或者回 Step 1 改描述。**锚点没确认,派生图会被拒。**配置为不确认时,一条命令出齐四张。
### Step 4 — 按需升档
```bash
node {baseDir}/scripts/character-refs.mjs gen <asset.json> --tier 3 --reason "E03 有发饰和领口特写"
```
`--reason` 写为什么升档,记进资产。也可以只出某几张:`gen <asset.json> detail-hair detail-neck`。
### Step 5 — 检查与重出
```bash
node {baseDir}/scripts/character-refs.mjs check <asset.json>
```
每张图过代码门(尺寸 300–5760、宽高比 0.4–2.5、比例对得上、≤20MB、背景够白),再看过期。
**有问题 exit 1。**门没过或过期的,单独重出那一张:
```bash
node {baseDir}/scripts/character-refs.mjs gen <asset.json> detail-neck --model codex
```
门里「背景够白」对全身图查四边,对大头照只查上边和两侧上半段(身体本来就顶到下边),细节图跳过并明说。
**门查不了长相、角度、取景**——出完一定要看图,这几项靠眼睛。
### Step 6 — 报告
```bash
node {baseDir}/scripts/character-refs.mjs render <asset.json>... --out <输出目录>/character-refs.html
```
多个角色可以一起出。语言默认取资产的 `lang`,临时换加 `--lang en`;非内置语言没带 `ui` 时,用 `--ui <文件>` 传自译文案,
缺文案会直接报错,不出半中半英的报告。界面文案换语言,角色描述保持原文。
第一档只有一张卡片;第二档起是设定图版面(左大头照,右上正面 / 侧面 / 背面,右下细节条),
**默认不显示细节图**,页面上一键切换。过期的图标红框,页面顶部有图例说明,鼠标移到红框图上能看到原因;混用模型会提醒。
### Step 7 — 汇报
一句话:出了哪几张、使用的哪个模型、报告路径。门没过或过期的点名。**没看过图不要说「效果很好」。**
---
## 落地结构
```
<输出目录>/
├── character-refs.html ← render 产出
└── <角色名>/
├── asset.json ← 角色描述 + 每张图的全部版本
└── default/ ← 造型(v1 只有 default 一套)
├── front-full.v1.png ← 锚点
├── face-front.v1.png
├── face-front.v2.png ← 重出不覆盖
└── detail-hair.v1.png …
```
每张 PNG 里都写着标识(`角色/造型/视图/版本`)、模型、参考了哪几张,图被单独拷走也查得到来历。
`asset.json` 的结构见 `references/schema.md`。
## 边界
- 实测过的画风只有写实和动漫两种(都是 Qwen 跑四档);自定义画风效果取决于写法,先出锚点看
- 细节图只收长在角色身上的(发饰、领口、袖口、鞋);道具归 novel-art
- 已知短板:小疤痕容易画成新伤、双排扣这类大面积细节取景偏远、Qwen 的领口细节会把粗布画得像丝绒(换 codex 重出这一张)、Qwen 细节图里露出的皮肤会偏老(提示词已加年龄句,16 岁样例上有效,未大量验证)
- `openai` 适配器只使用本地假服务器验过请求格式,**没有用真 key 跑过**;第一次使用时先出一张锚点看看
- 同一套提示词在 Qwen 和 GPT 上都实测过;自定义模型的效果取决于那个模型
## 自测
```bash
node {baseDir}/scripts/selftest.mjs
```
230 项断言,不调模型、不花额度:PNG 标识读写、检查门、输入校验、多语言确认表与报告、画风预设与换画风、提示词、过期传导、出图适配器的请求格式(Qwen 与 GPT Image 使用本地假服务器)、
以及使用自定义命令跑通的完整流程。改完脚本先跑这个。
## 自带样例
`{baseDir}/examples/阿禾-描述.txt` 是一段用户式的一句话描述(1920 年代江南采茶姑娘),
`阿禾-intake.json` 是拆好的输入,推断和默认的字段都标了。样例图不进版本控制,要看就使用自己的模型跑一遍。
@@ -0,0 +1,100 @@
# 提示词:哪些写法是实测出来的
提示词由 `buildPrompt`(`scripts/core.mjs`)按视图拼,**模型不写提示词,只填角色字段**。
改模板之前先读这里——每一条都是某一轮实测失败换来的。
实测环境:Qwen Image 2.1(ComfyUI,`TextEncodeQwenImageEditPlus` 挂参考图)和 codex 内置出图(GPT),
角色覆盖 19 岁民国女学生、70 岁老船夫、28 岁军人、16 岁采茶姑娘,多个种子。
## 总结构
| 视图 | 第一句 | 参考图 | 比例 |
| --- | --- | --- | --- |
| 正面全身(锚点) | Full-body front view: she stands naturally facing the camera… | 无,文生图 | 2:3 |
| 正脸大头照 | Zoom in to an extreme close-up head-and-shoulders portrait, passport-photo framing… | 锚点 | 4:5 |
| 90° 侧面 | Full-body side profile… both face the right edge of the image | 锚点 | 2:3 |
| 背面 | Rotate the camera 180 degrees around her… | 锚点 | 2:3 |
| 细节 | Zoom in to an extreme close-up of only … | 锚点(头发 / 脸 / 领口另挂大头照) | 1:1 |
| 45° 大头照 | 同正脸大头照,再加 45° 的几何描述 | 锚点 | 4:5 |
锚点把全部角色字段写成完整描述;派生图只写「做什么改动」+ 一句短的一致性清单 + 画风层。
## 改图模型只听第一句
参考图的构图力量很强,模型默认照着参考图重画一张差不多的。**只有第一句明确要求改构图,它才改。**
- 大头照:第一句必须是 `Zoom in to an extreme close-up head-and-shoulders portrait, passport-photo framing`,
再写可量化的取景:头顶贴近上边、下巴在画面高度正中、下边切在锁骨。只写 close-up 会出成半身
- **正面也要写成几何**:锚点写「肩、髋、脚都正对镜头,鼻子在脸的中线上,两只耳朵一样可见」,反向词禁 three-quarter view / turned body / contrapposto。
只写 facing the camera 时,动漫画风会按「官方角色图」的习惯把锚点画成微侧身,大头照照着锚点改,也跟着侧过去 20–30°,几乎和 45° 那张一样(实测)。
正脸大头照同样写几何、禁侧脸;**取景写在几何前面,结尾再重申一次取景**——几何写长了会冲淡开头的「拉近」,镜头退回半身(实测)
- 背面:`Rotate the camera 180 degrees around her`,再写**只有背面看得到的东西**(辫子垂在背后、衣服背面、脚跟),
反向词禁 face / buttons / pockets。不写这些 Qwen 会出成正面
- 侧面:写全身和头都朝画面右侧、只看得到右半边脸、不看镜头。不写「不看镜头」会回头看
- 45°:使用几何线索——鼻尖指向画面左侧、左耳可见、右耳隐藏。Qwen 上偏小(约 30°),GPT 更准
- 细节:第一句 `Zoom in to an extreme close-up of only <部位>`,再写画面边界(铺满画面、看不到脸 / 只看到脚和脚踝)
## 一致性清单要短,按视图取层
- **不要把锚点的长描述整段重复进派生图。**一句「Keep her exactly the same person as in the reference image: 脸、发型、上装、下装」就够
- **大头照只写脸、头发、上装。**写了下装和鞋,模型就把镜头拉远去画它们(实测:带上全部服装和四个细节,大头照出成了半身)
- **细节图不列整套服装**,只写一句 `Same person, same clothing and materials as in the reference image`。
第一轮细节图 8/8 失败,就是因为一致性那段列了整套衣服,模型照着画了整个人
- 细节图反向词禁 face / head / portrait / full body / standing figure / wide shot / medium shot;
挨着脸的细节(头发、脸上的东西)只禁 full body / whole face
## 写实(画风层「方案四」)
用户拍板的基线(2026-09-26)。整段在 `DEFAULT_LOOK`,`look-template` 打印出来:
- 不写 photorealistic、超高清、浅景深——前者会往 CG 渲染带。直接写 `A real photograph, not a render` + 全画幅、85mm、f/5.6、真实白平衡、不修图不美颜、轻微颗粒
- 光写成有方向的主光 + 弱补光(脸和衣服有体积),背景仍是打到纯白的无缝背景纸
- 反向词压 AI 感:CGI、塑料皮肤、蜡质皮肤、娃娃脸、过度对称、HDR、过饱和、平光
- **皮肤粗糙程度不放画风层**,放角色的 `skin`:统一写「泛红、小瑕疵」会让年轻角色出成病容
- **写光的效果,不写光源**:写 window 会把窗户画进画面
## 动漫(画风层 `anime`)
**默认画风**(用户 2026-09-26 定)。写实用 `--look 写实`。
2026-09-26 在 Qwen 上用阿禾跑完四档(9 张),全部过门,看图角度、取景、服装一致性都对:
- 写成「日式电视动画的官方角色图」:干净等粗线稿、一层柔影加少量硬高光的赛璐璐上色、平涂、动画式眼睛、简化的鼻子和嘴
- **不写 model sheet / turnaround / character sheet**——这几个词会让模型在一张图里画多个视图;反向词里还专门压了 character sheet、multiple views
- 反向词压写实:photograph、photorealistic、pores、film grain、3D render、semi-realistic
- `medium: drawn`:提示词里**不写角色的皮肤层**(毛孔、雀斑会把画面往写实拽),头发细节不写 macro photo
- 写实版里 Qwen 把领口粗布画成丝绒的问题,动漫版没有出现
- 45° 大头照同样转不够(约 20–30°),和写实版一样是 Qwen 的问题
画风层只管「怎么画」;「画谁」全在角色层。同一份阿禾的输入,换预设就能出两种画法,不用改任何角色字段。
## 细节图露出的皮肤
Qwen 画领口、袖口、脚踝时,露出来的皮肤容易画老(没有脸做参照,年龄感丢了)。
领口、袖口、鞋三类细节加了一句 `Any visible skin is the skin of a 16-year-old.`(年龄取角色字段)。
16 岁样例上袖口和脚踝的皮肤都对了。
## 自定义细节(2026-09-26,Qwen)
自定义细节按 `part` 套取景,和默认槽位同一个套路(第一句只写这个部位、写清画面边界):
- 手(指尖染绿)2/2、腰(短袄下摆压裤腰)2/2、身体(胸前钢笔)2/2 都出对了;钢笔取景偏宽,带到下巴
- **脸部局部(眼镜)全部失败**:三种写法(普通、写死「发际线下巴耳朵都在画外」、只挂锚点不挂大头照)× 2 个种子,全部画成整张大头照。
脸上的特征本来就在正脸大头照里看得清,所以不开放 `part: face`
## 已知短板
- 小疤痕:写成「愈合多年的旧疤:细、平、略浅、几乎不凸」有改善,仍偶尔像缝过线的新伤
- 双排扣这种大面积细节:两个种子取景都偏远
- 细节部位写太大(「斜襟和一排扣子」)会把整个前胸框进来——写小(「小立领和喉下一颗扣」)
- Qwen 的领口细节会把粗布画出褶皱反光、像丝绒(两个角色都出现过);GPT 出同一张材质是对的,领口不满意就 `--model codex` 重出这一张
- 同一组派生图里混用 Qwen 与 GPT:衔接得上,细节图之间有轻微亮度差
## 参考图怎么挂
- **一张图只参考锚点**,头发 / 脸 / 领口细节再加确认过的大头照。图与图之间不链式参考——链式会累积漂移,也会让单张重出牵连一串
- 大头照缺失或过期时,这几张退回只挂锚点,不阻塞,记录里留注
- 不用拼好的多视图大图当参考:单人时 Qwen 能认出是同一人,但会把景别往全身带、脸被摊小;分开挂最像
- **多人同框更不能用拼接图**(2026-09-26,Qwen 旧编码节点,两人两个镜头各 2 个种子):每人一张「大头照 + 三视图」拼接图,
4 张里 3 张画出 3–4 个人(拼接图里的每个视图被当成一个人),而且女主被男主吃掉。每人只挂锚点也会串:一个人的脸和衣服串到另一个人身上。
只有「女主大头照 + 锚点、男主锚点」这组 4/4 全对。拼接参考图因此没有做进 skill
@@ -0,0 +1,86 @@
# asset.json
一个角色一个文件,放在 `<输出目录>/<角色名>/asset.json`,图片路径都相对它。**由脚本读写,不要手改 `views`。**
要改描述:改 intake 重新 `new`(新资产),或直接改 `layers` / 造型里的文字——改完所有照旧文字出的图都会标成过期。
```jsonc
{
"name": "阿禾", // 只用于文件名、标识和报告,不进提示词
"source": "样例 · 茶山", // 可省
"lang": "zh", // 确认表与报告的语言;没有这个字段按 zh
"ui": { … }, // 只有非内置语言才有:自译的界面文案
"layers": { // 角色层:换造型也不变的
"identity": { "age": 16, "gender": "female", "en": "…", "text": "…", "source": "stated" },
"face": { "en": "…", "text": "…", "source": "stated" }, // en 进提示词;text 给人看(旧资产是 zh,照样认)
"hair": { … },
"build": { … }, // 可省
"skin": { … }, // 可省,省了按年龄补
"backCue": { … } // 可省,省了按发型与服装拼
},
"outfits": { // 造型层;v1 只有 default
"default": {
"label": "常态 · 采茶装",
"top": { "en": "…", "text": "…", "source": "stated" },
"bottom": { … },
"details": [ { "slot": "hair", "en": "…", "text": "…", "source": "stated" }, … ],
"overrides": {}, // 这套造型要改的角色层,比如换发型:{ "hair": { … } }
"look": { "id": "anime", "label": { "zh": "动漫", "en": "Anime", "ja": "アニメ" }, "medium": "drawn",
"style": "…", "clean": "…", "neg": "…" }, // 画风快照;medium: photo 照片 / drawn 画出来的
"upgrades": [ { "tier": 2, "reason": "E03 有面部特写", "at": "…" } ],
"views": {
"front-full": {
"current": 2, // 当前生效的版本
"versions": [ { … v1 … }, { … v2 … } ] // 重出只追加,不删
}
}
}
}
}
```
## 一个版本
```jsonc
{
"v": 1,
"id": "阿禾/default/front-full/v1", // 标识:角色/造型/视图/版本
"file": "default/front-full.v1.png",
"sha256": "…", // 写入标识之后的文件哈希
"model": "qwen", // 这张使用的模型;同组混用只提醒
"seed": 1980929016,
"prompt": "…", "negative": "…", // 实际发出去的提示词
"refs": [ { "view": "front-full", "v": 1, "sha256": "…" } ], // 实际挂了哪几张参考图
"notes": [], // 比如「face-front 缺失或已过期,这张只挂锚点」
"layersHash": "…", // 出图时的文字描述指纹
"lookHash": "…", // 出图时的画风快照指纹
"gates": [ { "id": "size", "label": "…", "ok": true, "detail": "1056×1584" }, … ],
"createdAt": "…",
"confirmed": true, "confirmedAt": "…" // 只有锚点有:false 待确认 / true 已确认 / "auto" 配置为不确认
}
```
同样的标识、模型、参考图也写进 PNG 的 iTXt 字段(`shuohao:id`、`shuohao:model`、`shuohao:refs`)。
## 视图
| id | 档位 | 比例 | 参考图 |
| --- | --- | --- | --- |
| `front-full` | 1(锚点) | 2:3 | 无 |
| `face-front` | 2 | 4:5 | 锚点 |
| `side-full` | 2 | 2:3 | 锚点 |
| `back-full` | 2 | 2:3 | 锚点 |
| `detail-hair` / `detail-neck` | 3 | 1:1 | 锚点 + 大头照 |
| `detail-sleeve` / `detail-feet` | 3 | 1:1 | 锚点 |
| `detail-<id>`(自定义) | 3 | 1:1 | `part` 为 hair / neck 挂锚点 + 大头照,其余只挂锚点;标「未实测」 |
| `face-45` | 4 | 4:5 | 锚点 |
## 过期
一张图的当前版本满足任一条就过期(`check` exit 1,报告标红框):
- 文字描述指纹变了:改了人本身的描述(身份、年龄、性别、脸、头发、身形、皮肤、上下装、背面)→ 全部过期;
改了某条细节 → 只有那张细节图过期;加、删细节 → 不影响其他图
- 画风快照变了
- 它参考的某张图换了版本(重出锚点 → 其余全部过期;重出大头照 → 挂了大头照的细节过期)
只标记,不删除;重出这一张就好。锚点重出后要重新确认。
@@ -0,0 +1,101 @@
# 一次性输入:怎么把一段描述拆成字段
用户一段话描述角色,你拆进 `intake-template` 的各字段,缺的自己补,然后 `intake-check` 出确认表给用户看。
**不要一项一项问。**只有年龄、性别、年代推不出来时才问——这三样错了,整组图都错。
## 语言
`lang` 是确认表和报告的语言,照用户说话的语言填:`zh` / `en` / `ja` 内置。其他语言(法语、韩语……):
```bash
node scripts/character-refs.mjs ui-template fr
```
把打印出的英文文案逐项翻译(占位符 `{n}` `{x}` 原样保留),整块放进 intake 的 `ui` 字段。缺一项 `intake-check` 就报错。
每个字段两段文字:`en` 进出图提示词,**不管 `lang` 是什么都写英文**(出图模型吃英文最稳);`text` 给人看,用 `lang` 的语言写。
旧输入写的是 `zh`,照样认。
## 字段
| 字段 | 进哪些图 | 写什么 |
| --- | --- | --- |
| `identity` | 全部 | 年龄(整数)、性别(female / male)、族裔、年代、身份。`en` 写成一句完整的人物介绍 |
| `face` | 全部 | 脸型、眼、眉、鼻、嘴、肤色、有辨识度的面部特征 |
| `hair` | 全部 | 发色、长短、发型、发饰 |
| `build` | 全身视图 | 身形、体态。可省 |
| `skin` | 全部 | 皮肤状态。可省,省了按年龄给缺省 |
| `outfit.top` | 全部(大头照只用这一项服装) | 上装,连同领口、袖子的状态 |
| `outfit.bottom` | 全身视图 | 下装与鞋袜 |
| `outfit.details` | 第三档 | 四个槽位 `hair` / `neck` / `sleeve` / `feet`,每个只写这个部位本身 |
| `backCue` | 背面 | 只有背面看得到的东西。可省,省了按发型与服装拼 |
服装拆上下两段,是因为**大头照只能带上装**:提示词里写了裤子和鞋,改图模型就会把镜头拉远去画它们。
## 来源标记
每个字段带 `source`:
- `stated` —— 用户原话里有
- `inferred` —— 你按身份、年代推出来的(1920 年代采茶姑娘 → 黑布裤、草鞋)
- `default` —— 惯例缺省(皮肤、背面)
**如实标。**确认表把推断和默认的项标出来,用户只看这几项就够了。把推断标成原话,等于替用户做了决定却不说。
## 英文怎么写
- 写能画出来的东西:「wary eyes」可以,「a girl who has been through a lot」不行
- **不写角色名**,不写画风词(anime、photorealistic、cinematic……)——画风层统一加,写进角色里会和它打架
- 小特征写成真实状态:疤写「愈合多年的旧疤:细、平、略浅、几乎不凸」,否则会画成粉红色的新伤
- 皮肤写到什么程度是角色属性,不是画风:统一写「泛红、有瑕疵」会让十九岁女生出成病容
- 族裔推不出来默认东亚,并落到具体五官上
## 细节图
**细节的内容是模型按角色理解出来的,不是固定的四样。**挑的顺序:
1. **用户点名的优先**(`source: stated`)——「细节要腰间的玉佩和左手的银戒指」
2. 没点名,挑**这个角色最能认出来的地方**:配饰、腰间挂的东西、特别的鞋、衣服上独有的补丁
3. 实在没有特别的,用下面四个默认槽位兜底
最多 8 个。**细节里的东西必须已经写在外貌或服装里**——细节只决定拍哪些特写,不给角色添新东西;
要加新配饰,先改服装字段(那会让整组图过期,因为人变了)。加、删、改细节本身,只影响那一张细节图。
### 默认槽位
四个,都是实测能稳定出来的:
| 槽位 | 写什么 | 例 |
| --- | --- | --- |
| `hair` | 发饰、辫梢、发髻的一处 | the knot of her faded blue cotton headscarf at the nape |
| `neck` | 领口与最上面**一颗**扣子 | the small standing collar and the single cloth-knot button at her throat |
| `sleeve` | 一只袖口 | the indigo cotton sleeve rolled up above the wrist, with tea stains on the edge |
| `feet` | 鞋与脚踝 | woven straw sandals and the trouser hems tied at the ankles |
只写部位本身,**不写「特写」「close-up」之类取景词**,取景由脚本加。
**部位要小到一眼看得全。**写「斜襟和一排布扣」,斜襟从领口一路开到腋下,模型就把整个前胸都框进来(实测);
写「小立领和喉下那一颗布扣」就对了。领口只写领子加最上面一颗扣子,袖口只写一只。
### 自定义细节
默认槽位之外使用 `slot: "custom"`,另给 `id`(小写英文)和 `part`。英文同样**只写部位本身**,取景由脚本按 `part` 加。
自定义细节在确认表和报告里标「未实测」,出完一定看图。
| part | 取景 | 实测(Qwen,2 个种子) |
| --- | --- | --- |
| `hands` | 只拍一只手,手占满画面 | 指尖染绿:2/2 |
| `waist` | 只拍腰部,不见脸和腿 | 短袄下摆压裤腰:2/2(粗布有丝绒似的反光) |
| `body` | 只拍这一处,不见脸和全身 | 胸前的钢笔:2/2(取景偏宽,带到下巴) |
| `hair` / `neck` / `feet` | 与同名默认槽位相同 | — |
**脸上的特征(眼镜、疤、痣)不出细节图**,写进 `face` 字段:正脸大头照里已经看得清;
Qwen 实测三种写法 × 2 个种子,全部画成整张大头照(校验会拦 `part: face`)。用户点名要脸部特写时,照这个说明。
其他实测不稳的:双排扣这种大面积的扣子(取景偏远)。
旧输入里以 `Zoom in to ` 开头写整句的自定义细节,照样认、原样使用。
## 细节只收长在角色身上的
发饰、领口、袖口、鞋、配饰。**道具不收**——皮箱、信、刀归 novel-art;角色图要「空手」,拿着东西的手会污染参考图。
@@ -0,0 +1,40 @@
# 角色一致性参考资料(外部学习)
来源:GitHub 开源项目(2026-10-04 抓取)
## shuohao-skills / character-refs(4095 star,Apache-2.0)
仓库:https://github.com/eternityspring/shuohao-skills
作者定位:「给任何故事里的角色真出参考图……先出一张正面全身锚点,
其余视图(大头照、90°侧面、背面、细节、45°大头照)**都只参考这张锚点**」
本目录:
- `01_skill_原文.md` —— SKILL.md(工作流 + 分档 + 冻结机制)
- `02_提示词规范_原文.md` —— **最核心**:每条都是实测失败换来的
- `03_角色卡schema_原文.md` —— 角色卡字段结构
- `04_字段拆解_原文.md` —— 一段话描述 → 分层字段的拆解规则
## 核心方法论(提炼,用于我们的实现)
1. **一组图只有一个根:正面全身锚点**;只有它是文生图,其余全是「锚点 + 一句改图指令」
2. **改图模型只听第一句** —— 派生图第一句必须是明确构图指令
(`Zoom in to an extreme close-up...` / `Rotate the camera 180 degrees around her`)
3. **正面要写几何** —— 「肩、髋、脚都正对镜头,鼻子在脸中线上,两耳一样可见」;
反向词禁 `three-quarter view` / `turned body`(不写几何,模型会画成微侧身)
4. **一致性清单要短、按视图取层** —— 大头照只写脸/头发/上装(写下装 → 镜头拉远);
细节图不列整套服装(列了 → 模型画整个人,实测 8/8 失败)
5. **不写 `character sheet` / `turnaround` / `model sheet`** —— 会让模型在一张图里画多视图
6. **写实画风**:写 `A real photograph, not a render` + 85mm f/5.6 + 真实白平衡 + 不修图不美颜 + 轻微颗粒;
**不写** `photorealistic` / 超高清 / 浅景深(会往 CG 带)
反向词压 AI 感:CGI、塑料皮肤、蜡质皮肤、娃娃脸、过度对称、HDR、过饱和、平光
7. **写光的效果,不写光源**(写 window 会把窗户画进画面)
8. **细节图露肤要带年龄**(`Any visible skin is the skin of a 16-year-old`)否则皮肤会画老
9. **模型不锁定** —— 一致性来自「同一张锚点」,不来自「同一个模型」
10. **冻结三样**:锚点 sha256 / 画风快照 / 描述指纹;变了就标「过期」,**只标记不删除**
## 用户提供的另一份资料要点
- 母版图要**摇一批候选**,挑**最有辨识度**的(不要指望观众记住"漂亮")
- **记忆点**:袖口卷到哪、发尾挑染、领子立起、左耳银钉(一致性检查的抓手)
- **纯灰 #808080** + **同一 seed** + 保存 job ID/seed
- **角色卡 5 硬字段**:脸型 / 发型发色 / 五官比例(可测量)/ 服装剪裁 / 固定配饰
—— 一旦定下,**所有提示词原样复用**("齐肩直发"→"中长发"会被当新角色)
- **分镜提示词结构**:角色字段前置占 **60%**,动作/场景/镜头后置 40%
- **防串脸**:双人镜头拆开;开 `input_fidelity`(可灵为 `image_fidelity`)