# 任务进度 / 踩坑记录 > 项目介绍和各代码文件说明见 [README.md](README.md)。本文档记录接口踩坑、 > 各批筛选与下载进度、已知问题与待办,随批次滚动更新。 ## 抖音视频批量采集 → 音频提取 → COS 上传 把一批 `douyin.com/video/...` 链接跑成 ASR 可用的数据集: **下载无水印 mp4 → 抽 mp3 → 探时长 → 传腾讯 COS → 出清单(jsonl / xlsx)**。 全程纯 Python,不依赖第三方解析服务、不依赖 JS 运行时、**不需要登录**。 --- ## 1. 结论先行:接口怎么走通的 > 这些结论的验证过程和完整时间线见 [RE.md](RE.md)(逆向全过程复盘)。 > 这里只放结论,改代码前先看。 | 路线 | 状态 | 说明 | |---|---|---| | web `/aweme/v1/web/aweme/detail/` | **要登录** | 匿名请求返回 HTTP 200 **空 body**。真实原因在响应头 `X-Whale-Throughput-Abort-Data`(base64 JSON):`{"content":"anonymous","name":"强制登录"}`。跟 a_bogus 签名对不对**无关**——伪造的随机 a_bogus 和完全不签名,表现一模一样。 | | App `/aweme/v1/aweme/detail/` | **可用,且不用签名** | 先打 `/service/2/device_register/` 拿 `device_id` / `install_id`,带上就能取详情。**不需要 X-Gorgon、不需要 Cookie、不需要登录。** 这是本项目默认路径。 | | CDN `play_addr` 直链 | 免鉴权 | `aweme.video.play_addr.url_list` 里的 douyinvod 地址没有防盗链,裸 `requests` 直接下。 | | `/aweme/v1/play/?video_id={uri}` | 免鉴权 | 302 跳 CDN,零鉴权。下载走的就是它。 | ### 两层限流(跑批量必然撞上) 1. **设备级 detail 配额**:单个 `device_id` 约 **575 次** detail 调用后被限,表现为 HTTP 200 空 body。 → `douyin/batch.py` 的 `DevicePool` 到配额就换设备;用 `generation` 标记当前设备,避免多个 worker 同时发现失败把设备连换好几次。 2. **IP 级注册配额**:短时间内注册约 **11~12** 次后,**所有** register host 一律返回 `device_id='0'`。 → `Device.register()` 指数退避重试(默认 5 次,30s 起步翻倍),让批处理**停下来等**,而不是空转着把剩余任务全标成失败。 第 2 条是踩过的坑:早期没有退避,一次注册受限直接烧掉 678 条任务。 ### 作品拿不到时怎么区分 看 `filter_detail.filter_reason`: - `images_base` → 图文作品(图集),本项目不处理,抛 `NotAVideo` - `status_self_see` / `status_audit_self_see` → 仅作者自见,实际不可用 --- ## 2. 目录结构 ``` ├── pipeline.py 【核心】一体化流水线:下载→抽音→上传→记录→删本地 ├── scripts/ │ ├── clean_ai.py 从链接 jsonl 里筛出 AI 相关作品,出链接清单 │ ├── jsonl_to_xlsx.py jsonl → xlsx(认 records/records.jsonl 和 records/manifest.jsonl 两种) │ └── export_sample_xlsx.py records.jsonl → 交付用样本 xlsx(支持 --ai-only / --limit) ├── tests/ │ ├── test_smoke.py 离线冒烟测试(不联网) │ ├── test_meta.py 标题/标签拆分、记录追加的测试 │ ├── test_aiclass.py AI 判定的测试(用例全取自真实数据) │ └── test_pipeline.py 流水线端到端测试(ffmpeg 真跑,网络和 COS 打桩) │ ├── douyin/ │ ├── app.py 【核心】设备注册 + App 端 detail │ ├── batch.py 并发批处理、DevicePool 设备轮换 │ ├── download.py 带 Range 续传的下载、去重 Registry │ ├── meta.py 标题/标签拆分、records.jsonl 追加写 │ ├── aiclass.py 判定作品是不是 AI 相关,分四档,见 §4.5 │ ├── cos.py COS 上传 + 上传后大小校验 │ ├── audio.py ffmpeg/ffprobe 薄封装:找可执行文件、拼命令、探时长 │ ├── urls.py 短链还原、aweme_id 提取 │ ├── client.py Aweme/DouyinError 等共用类型 + web 后端请求路径 │ ├── signer.py a_bogus 签名(仅 web 后端用,当前跑不起来,见下) │ └── crypto.py SM3 / RC4 / 自定义 Base64(未完成的纯 Python 签名器的积木) │ ├── data/ 源数据(外部抓来的,clean_ai.py 的输入。删了没法重新筛,别动) │ ├── 抖音链接20260827_40087.jsonl 40087 条 │ ├── 抖音ai视频_20260831_87110.jsonl 87110 条(其实是露营帐篷爬取,AI 占 3.3%) │ ├── 抖音ai视频_20260901_57261.jsonl 57261 条(同上,AI 占 2.8%) │ ├── 抖音ai视频_20260902_42391.jsonl 42391 条(9 月 AI 批,AI 占 90.9%) │ ├── 抖音ai视频_20260904_62171.jsonl 62171 条(同上,AI 占 88.3%) │ ├── 抖音ai视频20260908_118469.jsonl 118469 条(同上,AI 占 95.1%) │ ├── 所有作品.csv 4839 条,字段与 jsonl 不同,见 §4.5 第 8 条 │ ├── records/ 流水线产物与断点记录 │ ├── records.jsonl 全字段元信息(含 raw),断点续跑靠它 │ ├── pipeline_failed.txt 失败清单:aweme_id / 阶段 / 原因(重跑据此续传) │ └── manifest.jsonl 老产物:第一批 5393 条的 3 字段清单,见 §5 │ ├── exports/ 交付/导出产物(xlsx / csv) ├── lists/ 各批的链接清单,也是"哪批下了什么"的凭据 └── logs/ 各批跑批日志 `work/` 是流水线的临时目录,跑的时候自建、传完即删,仓库里不留。 **web 后端目前是跑不起来的,别把它当后路。** 具体状态: - `client.py` 里 `Aweme` / `DouyinClient` / `DouyinError` 这几个类型被 `app.py`、`batch.py` 正常用着,**不是死代码**;只有它的 web 请求路径闲置。 - `signer.py` 两个后端都不可用:`NodeSigner` 要 `node_signer/sign.js`,**这个目录不在项目里**; `ABogusSigner` 是纯 Python 实现,它自己的文档写着"剩字节拼装顺序待从 ab.js 逆出来",没写完。 - `crypto.py` 的 SM3 / RC4 / 自定义 Base64 对过官方测试向量(`test_smoke.py` 每次都验), 但它只是那个**没写完的** `ABogusSigner` 的积木,`signer.py` 并不 import 它。 所以真要走 web 端,得先补 `node_signer/`(或者把 ab.js 逆完)**再**准备登录 Cookie。 App 端被封的话这里没有现成退路,得先做上面这件事。 **本地媒体文件已全部删除**(`downloads/` 的 5393 个 mp4、`audio/` 的 5393 个 mp3),东西都在 COS 上。旧的分阶段脚本依赖本地文件,要用得先重新下载;`pipeline.py` 不受影响——它本来就是边跑边删。 输入链接文件 `all_video_links(1).txt` 也已不在,重跑需要重新提供。 --- ## 3. 环境 ```bash pip install -r requirements.txt ``` 另需系统里有 `ffmpeg` / `ffprobe`。实测环境:Python 3.11.0 + ffmpeg 2025-07-31 (gyan.dev full build)。 COS 凭证走环境变量,别写进命令行(会留在 shell history 里): ```powershell $env:COS_SECRET_ID = "..." $env:COS_SECRET_KEY = "..." ``` --- ## 4. 一体化流水线(推荐) ```bash $env:COS_SECRET_ID="..."; $env:COS_SECRET_KEY="..." # 先小批试跑,加 --keep-local 不删本地文件,人工看一眼产出 python pipeline.py --from-file links.txt --region ap-chongqing --limit 20 --keep-local # 确认没问题再放开 python pipeline.py --from-file links.txt --region ap-chongqing --workers 8 # 出 xlsx(--columns full 带上标题、作者、标签、发布时间) python scripts/jsonl_to_xlsx.py -i records/records.jsonl --columns full -o "exports/抖音-四川话.xlsx" # 出交付样本 xlsx(--ai-only 只导 AI 档,--limit 限量) python scripts/export_sample_xlsx.py --ai-only -o "exports/AI视频样本.xlsx" ``` 每条视频端到端走完 9 步才算完:下载 → 抽 mp3 → 探时长 → 校验 → 传两个对象 → 核对大小 → 写 record → 删本地。**磁盘峰值只有 `workers × (mp4+mp3)`, 约 100 MB,与总量无关**——分阶段跑得先摊 53 GB。 元信息全进 `records/records.jsonl`,一行一条,`raw` 内嵌,字段见下。断点续跑靠它, 重跑同一批会跳过已完成的(连下载都不会发生)。COS 上同名对象一律覆盖。 `pipeline.py` 的 `--records` / `--failed` 默认就指向 `records/` 目录。 ### 两道闸门 删本地文件是唯一不可逆的操作,所以卡两道,任一不过就保留文件、记 failed、继续下一条: 1. **抽取闸门** —— mp3 用 `ffprobe -count_frames` **实际解码**算时长,与 mp4 比对, 差超过 `max(1s, 2%)` 判定截断。 注意不能用 `format=duration`:它读的是 LAME 的 Xing 头,声明的是原始总帧数。 实测把 mp3 截断到 20%,`format=duration` 报的仍是完整的 10.0078s,一字不差; `ffmpeg -f null -` 同样拦不住(rc=0、无 stderr)。只有实解码能发现。 2. **上传闸门** —— 不信 `upload_file` 没抛异常,而是 `head_object` 取回 `Content-Length` 跟本地字节数比对,两个对象都对上才删。 ### records.jsonl 字段 ```json {"aweme_id":"...","title":"剥掉标签的纯标题","desc":"服务端原样", "tags":["..."],"author":{"nickname":"...","uid":"...","sec_uid":"..."}, "create_time":1582635538,"create_time_str":"2020-02-25 21:38:58", "duration_sec":41.702993,"duration_ms_api":41750, "stats":{"digg":0,"comment":0,"collect":0,"share":0,"play":0}, "music":{...},"cover_url":"...","video":{"uri":"...","width":1280,"height":720}, "cos_mp4":"cos://...","cos_mp3":"cos://...","size_mp4":0,"size_mp3":0, "source_url":"...","fetched_at":0,"raw":{ ...服务端原始 JSON... }} ``` 三条要注意的: - **`aweme_id` 必须是每行第一个字段**。续跑扫描用正则从行首抠 id,不解析整行 (几百 MB 的文件差别是 ~1 秒对十几秒)。重排字段不会报错,只会让启动悄悄变慢。 - **`duration_sec` 和 `duration_ms_api` 不是一回事**。前者是 ffprobe 实测,后者是 服务端声明值,实测同一条 41703 vs 41750 ms。切分要用 `duration_sec`。 - **`tags` 取自 `desc` 的正则全集,不是 `text_extra`**。实测 12 条样本里有 2 条的 标签在 `desc` 里有、`text_extra` 里没有,以后者为准会静默丢标签。 `raw` 内嵌导致单行几十 KB、整个文件十几 GB,所以读取一律逐行流式, `jsonl_to_xlsx.py` / `export_sample_xlsx.py` 已经改成流式了。 --- ## 4.5 从链接 jsonl 里筛 AI 作品 > 判定规则的逐条详解(词表选词标准、误收数据、否决清单、维护指南)见 [AI.md](AI.md)。 `clean_ai.py` 吃的是外部抓来的链接 jsonl(带 desc / hashtags / author / ai 等字段), 判定逻辑在 `douyin/aiclass.py`,分四档,取最强的一档: | 档 | 依据 | 含义 | |---|---|---| | `platform` | `ai.is_aigc` | 抖音自己打的 AIGC 标 | | `strong` | 文案/标签有 AIGC、AI 生成工具名、ai+创作类词、AI 赛事标签 | 作品声明是 AI 做的 | | `topic` | 只提到 AI 本身(ai 教程、人工智能、大模型、ChatGPT) | 在讲 AI,不一定是 AI 做的 | | `author` | 只有作者昵称或音乐标题带 AI | 最弱,会误收 | ```bash python scripts/clean_ai.py -i 抖音链接xxx.jsonl --stats # 先看分布 python scripts/clean_ai.py -i 抖音链接xxx.jsonl --tier platform,strong -o ai # 出 ai.jsonl + ai.txt python pipeline.py --from-file ai.txt --region ap-chongqing # 直接接流水线 ``` **要注意的**(都是从真实数据里挖出来的,改规则前先看 `test_aiclass.py`): 1. **`platform` 是唯一能抓到"作者没标但确实是 AI"的信号。** 实测 1744 条被平台标了 AIGC,文案标签里一个 ai 字都没有(脑筋急转弯、历史解说、原创动画)。纯关键词过滤 会整片漏掉。反过来 9000+ 条有明确 AI 信号却没被平台标——两路互补,都得要。 2. **`Al`(大写 A + 小写 L)冒充 AI。** 标签 `抖音al` 出现 129 次,文案里也有 `#Al创作`。 不归一化就漏。反过来 `ai` 是 email/detail/again/hair 的常见片段,只能认前后不接 字母数字的独立 `ai`。 3. **音乐标题默认是 `@<作者昵称>创作的原声`**,40087 条里 32555 条是这个壳。昵称带 AI 会连"创作"一起命中,等于让作者昵称从 music 这条路偷偷升到 `strong`——实测 771 条 这样混进来。现在先剥壳,剥完只当弱信号(全库剥完只剩 16 条还有命中,本来就没价值)。 4. **中文泛词不单独成立。** `动画`/`绘画`/`分身`/`动漫` 必须跟 AI 词同现,否则 830 条 `#原创动画` 全被误收。`剪映`(375)/`关键帧`(314)/`蒙版`(203)/`转场` 是剪辑术语,不是 AI。 5. **工具名不含 ai 字样的一大票**:即梦 / seedance / libtv / flovaai / 小云雀 / runninghub / oiioii / shotlab / veo3,只能靠名单,名单要跟着平台活动更新。 6. **一批工具名本身是常用词,必须要求跟 ai/aigc 同现**(见 `_AMBIG_GEN` / `_AMBIG_CHAT`)。 实测误判:豆包 224 条(狗名、豆沙包)、万相 17 条(《龙神万相》)、海螺 13 条 (三峰的"海螺"帐篷)、元宝 11 条(316 不锈钢元宝勺)、星流 1 条("明星流量"子串)、 清影 1 条("起舞弄清影")。而真命中的**无一例外**都同时带 AI 字样,所以这道门槛 不掉召回——旧文件重跑 15218 → 15204,掉的正好是这些。 7. **英文工具名要挡词中词,但只挡字母,不挡数字和中文。** `udio` 藏在 `Grass Studio` 里 17 次、`flux` 藏在 `Fluxxwave` 曲名里。可是数据里满地都是 `Seedance2.0`、`sora2`、 `runninghub0.21`、`libtv创作者`、`flovaai超创`,一刀切加 `` 会丢 3500+ 条真命中。 8. **别放开"数字人"/"换脸"/"分身"当独立信号。** 数字人民币含"数字人"、微信分身含"分身"、 穿搭视频也说"这套穿搭能换脸"。实测放开只进误判,一条真的都捞不到。 同理否掉的还有:lora(撞 valorant)、tripo(撞 tripod 三脚架)、seaweed(海藻)、 度加("亏损幅度加大")、万彩("29万彩礼")、生数("诞生数副名场面")、节点("关键节点")、 抽卡(原神)、捏脸(燕云十六声)、国漫(《剑来》真人制作)、对口型(真人对口型)。 这 8 条否决理由都写成了测试,见 `test_rejected_candidates_stay_none`。 9. **AI 还有第三种冒充写法:罗马数字。** `#AⅠ漫剧` 用的是 U+2160,跟 `Al` 一个套路。 10. **csv 的原声标题多一截 ` - 昵称`**(`@X创作的原声 - X`),剥壳正则要一起覆盖, 否则第 3 条那个作者昵称升档的 bug 会在 csv 输入上复发。 ### platform 档要不要求文案佐证 抖音给 **AI 配音 / AI 封面**的普通带货视频也打 AIGC 标。露营帐篷那两批里, `platform` 档有一半(2045 条)文案和作者昵称都零信号,点开是"四脚伞帐篷带广告位" "抖音私密账号是什么"。`--platform-needs-text` 就是卡这个的,作者昵称带 AI 也算佐证。 **但这个开关会误伤一类真 AI**:@栖光 那种做 AI 古风美图的,文案只写 `#古风 #国风古韵 #意境`,一个 AI 字都不会写。这类只有平台标签能抓到。 所以无佐证的那批不是垃圾,得按"是不是带货"再分一刀(见 `lists/platform无佐证_*.txt`)。 ### 抖音的 AIGC 字段:认 risk_infos.type,别认 is_aigc_media `raw.risk_infos.type` 是数字码,一一对应: | type | content | 含义 | |---|---|---| | 44 | 作者声明:内容由 AI 生成 | 作者主动声明,19946 条 | | 47 | 疑似使用了 AI 生成技术,请谨慎甄别 | 平台检测,措辞不确定,515 条 | | 76 / 328 | 作品含 AI 生成内容 | 平台判定,448 条 | **这几个才是 AI。**下面这些是别的免责声明,跟 AI 无关: | type | content | 条数 | |---|---|---| | 45 | 作者声明:虚构演绎,仅供娱乐 | 868 | | 336 / 351 | 个人观点,仅供参考 | 111 | | 54 | 作者声明:内容来源于网络 | 13 | | 335 | 作者声明:内容含营销推广信息 | 10 | **已知 bug**:爬虫那层把「`risk_infos` 有内容」就当 `is_aigc=true`,把上面这些 免责声明也算进 `platform` 档了,全库误算 **1029 条**。修法见 §7。 另外 `raw.is_aigc_media` 全库 24611 条**全是 `false`**,名字最像却完全没用,别信。 ### 各批筛选结果 | 源文件 | 总条数 | AI 占比 | 出的清单 | |---|---|---|---| | 抖音链接20260827_40087.jsonl | 40087 | 39.1% | `ai_videos.txt` 15218 | | 抖音ai视频_20260831_87110.jsonl | 87110 | **3.3%** | `ai_87110_严格.txt` 1366 | | 抖音ai视频_20260901_57261.jsonl | 57261 | **2.8%** | `ai_57261.txt` 776 | | 抖音ai视频_20260902_42391.jsonl | 42391 | 90.9% | `ai_42391.txt` 37703 | | 抖音ai视频_20260904_62171.jsonl | 62171 | 88.3% | `ai_62171.txt` 52360 | | 抖音ai视频20260908_118469.jsonl | 118469 | 95.1% | `ai_118469.txt` 106447 | | 所有作品.csv | 4839 | 未筛 | `csv_links.txt` 4839(全量下的) | 前两个文件(87110 / 57261)名字叫"ai视频",**实际是露营帐篷带货爬取** (标签 TOP:户外露营 10568、帐篷 9346),AI 只占 3% 左右。9 月的三批 (42391 / 62171 / 118469)才是真 AI 池,且 platform 档占绝对大头——这三批 `allow_download=false` 里 `aigc_forbidden` 多达 2.8 万 / 3.7 万 / 7.5 万条, 但 App 端接口不受此限(见下)。拿到新文件先跑 `--stats` 看一眼,别信文件名。 `aigc_forbidden` 那道限制**只作用于网页端,App 端接口不受影响**——已用 20 条探针 验过,9 条 web 侧 `allow_download=false` 的全都正常下下来了。不用 `--exclude-forbidden`。 ### lists/ 里各清单的关系 | 清单 | 条数 | 是什么 | |---|---|---| | `all_video_links(3).txt` | 3557 | 第一批输入 | | `ai_videos.txt` | 15218 | 40087 批筛出的 AI | | `csv_links.txt` | 4839 | 所有作品.csv 全量(未筛) | | `ai_87110_严格.txt` / `ai_57261.txt` | 1366 / 776 | 露营两批筛出的 AI | | `ai_42391.txt` / `ai_62171.txt` / `ai_118469.txt` | 37703 / 52360 / 106447 | 9 月三批筛出的 AI(platform+strong 档) | | `ai_合并待下载.txt` | 66411 | 62171 全量 + 42391 部分 + 118469 无,合并去重后的跑批输入 | | `ai_待下载.txt` | 2611 | 旧池剩余(platform 无佐证两档 + 零星),已下过 99%,基本作废 | | `platform无佐证_非带货.txt` | 776 | AI 古风/漫剧/语录图文 | | `platform无佐证_带货.txt` | 1331 | 待下(不建议):帐篷等实拍带货 | 截至 2026-09-29,9 月三批的下载进度都是 97%+(118469 批 103702/106447、 42391 批 37254/37703、62171 批 50989/52360),三批合计还有约 4565 条 不在 records.jsonl 里(含图文、已删除等下不动的)。 ## 5. 自检 ```bash python tests/test_smoke.py # 离线冒烟:SM3/RC4/base64 自测、id 提取、Registry python tests/test_meta.py # 标题/标签拆分、records.jsonl 追加与续跑 python tests/test_aiclass.py # AI 判定(用例全取自真实数据) python tests/test_pipeline.py # 端到端:ffmpeg 真跑,网络和 COS 打桩 ``` ### 老产物 manifest.jsonl `records/manifest.jsonl` 是第一批 5393 条四川话数据的清单(`cos_mp4` / `cos_mp3` / `duration` 三字段), `exports/抖音-四川话.xlsx` 就是它的 xlsx 版。**它和 `records/records.jsonl` 零重合**——那 5393 条不在 `records/records.jsonl` 里,所以这个文件是这批数据在 COS 上的唯一记录,别删。 生成它的那套分阶段脚本(`main.py` / `extract_audio.py` / `probe_durations.py` / `upload_cos.py`)已经删掉,功能全被 `pipeline.py` 覆盖了。这个项目没有 git,删了就是真没了—— 但那四个脚本做的事 `pipeline.py` 一条命令全包含,唯一不能复现的是 manifest.jsonl 的三字段老格式。 其中 `probe_durations.probe()` 被 `pipeline.py` 用着,已搬进 `douyin/audio.py`。 --- ## 6. 跑批结果 `records/records.jsonl` 累计 **219144 条 / 8676.02 小时 / 2483.43 GB(mp4)**, 全部在 `cos://asr-dig-data-1253181912/0005/zh/`。 9 月三批 AI 池(42391 / 62171 / 118469)已基本下完,各批进度 97%+。 早期按 AI 判档的拆分(platform 有佐证 5423 条 / strong 9036 条等)是 9/1 时点的 快照,那之后 records.jsonl 又进了十几万科目的 9 月 AI 批,旧数字只作历史参考。 ### COS 前缀 0005/zh/ 混了八批 ``` 5393 四川话 只在 manifest.jsonl 里,不在 records.jsonl 3517 第一批链接 14884 AI 筛选批(40087 源) 4813 csv 全量批 2093 87110 + 57261 两批 37254 42391 批 50989 62171 批 103702 118469 批 ``` **按前缀取数会一次拿到全部**,要区分只能走 `records.jsonl`。 ## 7. 待办 / 已知问题 **该修的** - **`is_aigc` 把免责声明当成了 AIGC**,全库误算 1029 条(见 §4.5)。爬虫那层应改成 `risk_infos.type in {44, 47, 76, 328}`,并把 `type` 一起存进记录——这样才能区分 「作者声明」(44) 和「平台检测」(47),两者可信度不同。改完 `platform` 档会掉约 1029 条, 真 AI 一条不少(那些走 strong)。 - **图文作品(`aweme_type` 68/61/55/163)不支持**,需要 `images[]` 分支。这是目前失败 的绝对大头:87110 批 38 条失败里 33 条是图文,AI 池里累计 241 条下不了。 图文在按 aweme_id 排序的清单里**扎堆在头部**(早期 AI 内容多是 Midjourney 出图), 所以跑批前几百条失败率会飙到 15%,后面掉到 2-3%,别被吓到。 **已知取舍** - 最长一条 3398.988 秒(约 57 分钟),若 ASR 侧对单条时长有上限需要另行切分 - `split_desc` 在标签名以 `#名字` 形式也出现在正文里时会连正文一起删(见 `test_split_repeated_tagname_in_body_is_lossy`);服务端给了可信偏移时不会 - 设备配额轮换的触发条件是**每 550 次 detail**,但到底是按 IP 还是按 UA/设备指纹限流 一直没验证过。实测跑到第 3 次轮换都正常,暂时不用管 - `topic` / `author` 两档从没进过下载清单。`author` 档现在 14 条**全部来自音乐标题** (`【AI配文风景滤镜】的原声` 这类抖音模板),作者昵称一条都没有——因为凡是作者叫 AI 的, 作品本身也都标了 AI,昵称这条线提供不了增量,它的价值只是给 `platform` 档做佐证 **安全** - 聊天记录里出现过一组 COS 密钥,**建议轮换**。凭证只能走 `COS_SECRET_ID` / `COS_SECRET_KEY` 环境变量,`CosUploader` 不读命令行参数