Community编程与开发github.com

toufiqfarhan0/chronicle-ink

将一个主题转化为一部经过事实核查的 2-3 分钟知识影片,画面完全由你编写的代码绘制。

chronicle-ink 是什么?

Chronicle·Ink 是一款为 AI 智能体(如 Claude Code、Cursor 等)设计的技能,让你仅通过文本 LLM 和工具即可生成一部完整的知识影片。该技能的核心是:智能体编写场景代码,通过 Canvas 2D 绘制每一帧画面,并结合 CLI 工作流完成研究、事实核查、脚本编写、音乐生成、渲染和审查。它支持三种风格包:中国水墨卷轴(ink)、羊皮纸地图集(roman)和白色城市模型(model)。尤其是用于城市题材的“模型”风格,能从全球视角飞入真实城市,以 3D 形式展示每栋 OpenStreetMap 建筑的真实高度,并通过光效、时间线推进和旁白同步特效(如河流、城墙、火灾、链接、重建)来讲述城市历史。所有事实必须经过至少两个独立来源的交叉验证,确保可靠性。该技能完全在本地运行,仅依赖 Node.js、FFmpeg 和浏览器引擎,不调用任何多模态生成模型,因此成本仅为你自己的 token。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/toufiqfarhan0/ai-city-remix-workflows/tree/main/chronicle-ink/chronicle-ink

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Chronicle · Ink(水墨卷轴)

你(正在阅读本材料的智能体)是整个摄制组。本技能为你提供:一个分阶段 CLI、一个事实引擎、一个代码合成系列主题及其精确节拍映射、一套程序化音效包、一套水墨绘制工具包和一个 HyperFrames 渲染器。你来撰写声明、带有单行字幕的镜头列表、场景代码和审查意见。不调用任何图像、语音、音乐、视觉或 LLM 模型;唯一的成本是你自己的 token。

硬性规则:仅限文本 LLM 加工具。 所有写作、研究、事实核查、场景代码和审查均由运行此技能的文本 LLM 执行(例如通过 SandBase 的 MiniMax M3.1 flash,或任何其他智能体模型)。你可以调用工具:网页搜索与获取、SandBase 数据 API、以下命令、FFmpeg。你绝不能调用多模态生成模型:没有图像、视频、TTS/语音、音乐或视觉模型,也没有 AI 生成的图片或音频文件。每一帧都由你编写的场景代码绘制,每一个声音都由引擎合成。审阅样片时,只能通过数字和你自己渲染的帧来进行,而非视觉模型。使用了多模态模型的影片不是 chronicle-ink 影片。

选择性例外:音乐和旁白。 仅当用户明确要求时,主题可被替换为 music/ 目录下由 music/track.json 描述的音频文件:可以是公开许可的录音(公有领域、CC0、CC BY 3.0/4.0;绝不可使用 NC、ND 或 SA),或由 SandBase 音乐模型生成的器乐(例如 minimax/music/2.6,设置 is_instrumental: true,仅通过 sandbase CLI 或 API 使用)。图片、字幕、音效及其他所有规则保持不变;片尾字幕将注明该曲目,不再说明音乐由代码生成。参见 references/music-track.md。同样,可以添加画外音:每个字幕对应一个来自 SandBase 语音模型的片段,准确说出字幕内容(voice/voice.json,参见 references/voice.md)。

风格包

brief.style(或 new-project.mjs --style)选择外观;引擎、事实和检查都相同。

风格外观资源包用途
ink(默认)中国水墨卷轴,LXGW WenKai,“水墨主题”assets/style-pack.json人物、地标、动物
roman羊皮纸地图集,Cinzel,柔和 D-多利亚主题assets/styles/roman/城市、古典主题
model暗色工作室中的白色城市模型,历史投射为光效,全球飞入,照片卡片,“地图集主题”assets/styles/model/城市生命故事:阅读 style-model.md

方法是公开的“Vibe 知识展示”配方:设定规格 → 在节拍上剪辑 → 为每个关键动作配乐 → 逐段渲染并在继续前修复每个片段。

规格(默认值)

长度seconds 120、150(默认)或 180;每 25–35 秒重新设置钩子;4–6 / 5–7 / 6–8 个知识点
格式16:9 1920×1080 30fps(默认),可选的 9:16 1080×1920
语言brief.lang = 任何 BCP-47 标签(默认 en;zh-CN 为头等支持)。一份剧本服务多种语言
旁白无语音:每个镜头一句简短字幕说明图片内容,绑定到经过验证的声明,关键词高亮
音乐风格的系列主题(水墨:“水墨主题”,D 宫五声调式,古琴/箫音色),总是 88 BPM,段落顺序为 前奏 · 主歌 · 铺垫 · 副歌 · 间奏 · 铺垫2 · 副歌2 · 尾奏;可选的 music/track.json(music-track.md)
响度−14 LUFS 综合响度,≤ −1.5 dBTP(两遍 loudnorm)
标签0–3 秒显示“AI 生成”徽章,片尾画面包含 AIGC 声明,MP4 中包含 AIGC 元数据

需要 Node 22+、FFmpeg/ffprobe 和 Chrome(HyperFrames 0.8.30 通过 npx 运行;渲染始终使用 CPU 路径,确保画布输出在任何机器上都一致)。运行 node scripts/chronicle.mjs doctor。

城市影片(主题 city,结构 life)

针对“每座城市都有一段生命故事”的用例:一个 Wikidata 上的城市(坐标 + 人口)锁定为 city,并使用生命结构:诞生(河流、建城、命名)→ 成长(建成区在地图上扩展)→ 首次高度 → 伤痕(劫掠、火灾、瘟疫、战争)→ 重生 → 今日(同一片地图,今昔对比)→ 回响(可选作者的个人独白“我与这座城市”)。阅读 narrative-city.md。

地图是真实的地理数据,而非模型图片:

  1. 在项目中编写 geo/places.json(中心和地点通过 Wikidata QID 指定,历史区域使用近似半径),然后运行 node <skill>/scripts/geo.mjs --project <p>。它会从 Wikidata 读取坐标,从 OpenStreetMap(无密钥的公开读取)读取河流、水域、海岸线、铁路和今日主要道路,保存到 geo/basemap.json。
  2. Ink 风格:将 assets/style/exemplars/city-map.js 复制到 scenes/ 目录,并通过其参数(视角、区域、地点、道路、年份戳、伤痕)驱动每个地图镜头。场景将数据作为 s.geo 读取,并使用 s.kit.cityMap() 绘制;每一帧都带有 “© OpenStreetMap contributors (ODbL)” 版权声明。
  3. 历史范围是近似值:cityMap 始终标注为 “AI 示意 / AI illustration”。年份戳和地图标签只能显示声明或一级来源中出现的日期和名称。
  4. 模型风格:在 geo/places.json 中添加 chronology 块(摄像机、时代、范围、城墙、事件、地标),使用 node <skill>/scripts/photos.mjs --project <p> 获取地标照片,并使用单一的 city-3d 场景(WebGL 中的真实建筑和高度,一条连续的摄像机路径)。参见 style-model.md 和逐步指南 city-film-recipe.md(了解罗马、纽约和巴塞罗那样本的制作方式)。

此机器无法访问的页面(Wikipedia 常被屏蔽):请使用你自己的工具获取,并将文本保存到项目的 sources/<n>-<name>.txt 文件中(第一行注明 source: <url>)。后续的 all 命令会读取该文件夹;文件名保持来源 ID 稳定。

快速开始

node <skill>/scripts/new-project.mjs --out /abs/films/su-shi --topic "苏轼" --film-lang en
node <skill>/scripts/chronicle.mjs all --project /abs/films/su-shi

all 执行 研究 → 事实 → 剧本 → 时间线 → 音乐 → 场景 → 渲染 → 检查 → 报告,并在你需要编写任何内容时停止(状态为 status: awaiting_…)。阅读项目中的 AGENT_TASK.md,编写它要求的文件,然后重新运行 all。项目必须位于技能文件夹之外。

剧组角色

角色你的职责命令读取写入
导演负责 brief,运行片段循环,决定何时一个片段完成all,render --segmentbrief.json,样片work/review.json
研究员无限制搜索(使用你自己的搜索工具);传递有用的页面research --add-source <url> / --source-file <file>Wikidata (QID 锁定),Wikipedia,一级网站work/research/
事实核查员提取带有逐字摘录的原子声明;为每个字幕回答蕴含关系facts,script来源片段,entail.request.jsonwork/facts/claims.agent.json,work/script/entail.agent.json
编剧根据主题的段落编写镜头列表scriptscript.request.json(计划、声明、规则)script.json
美术使用水墨工具包编写场景代码;自我审查帧scenes时间线,references/style-pack.md,示例scenes/<name>.js
作曲无需作曲:主题已固定;你为每个动作选择音效层级musicbrief.secondswork/music/theme.wav,beatmap.json
剪辑渲染片段和最终影片render场景、主题、音效提示out/segments/,out/film.mp4
QA运行检查,阅读连接表,修复或重新运行check,report影片、时间线、节拍映射、审查work/check.json,out/report.*
地图动画师编写 geo/places.json,运行 geo 命令,驱动 city-map 镜头node scripts/geo.mjsWikidata P625,OpenStreetMapgeo/basemap.json

镜头列表字段(script.json,schema chronicle-script/2)

bars(1–4 个整小节的一段,因此每次切换都在强拍上)· size(广角/中景/近景/特写)· camera(固定、推近、拉远、左摇、右摇、上升、视差)· transition(切、水墨晕染、卷轴平移、印章)· scene + params· description· motion· sfx({ beat, kind, tier, dur_beats })· kind + claims(+ hedge)· caption.<lang>.text + em(关键词)· kp· rehook。完整格式和规则:work/script/script.request.json 和 pipeline.md。

事实优先

  1. research 将主题锁定为一个 Wikidata QID,直接使用无密钥的公开读取访问 Wikipedia 和一级网站(CBDB、Britannica、UNESCO、GBIF、博物馆)。你可以在任何地方搜索,但只有经过 facts 交叉检查后的事实才能进入影片:至少两个独立发布者一致同意,至少一个为一级来源 → verified;一个来源 → single-source(仅限有保留的字幕,不超过 2 条,绝不在引言或副歌中使用);冲突 → disputed(“一家之言”)。八卦网站只是线索,不能作为证据。
  2. 如果此机器无法访问某个网站,请使用你自己的获取工具阅读,并将文本逐字保存到一个文件中,第一行为 source: <url>;通过 --source-file 传递。
  3. 每个字幕、卡片、标题和片尾行都绑定声明 ID;行中的数字、日期和名称必须在其声明中;引用需要一条已验证的引用声明。然后,你仅根据声明文本诚实回答蕴含关系。参见 factcheck.md。

主题上的故事

计划(work/script/plan.json)以小节数提供主题的段落(150 秒:前奏 4 · 主歌 12 · 铺垫 4 · 副歌 8 · 间奏 8 · 铺垫2 4 · 副歌2 8 · 尾奏 7;120/180 秒则在段落边界重复或修剪小节),以及重新设置钩子的小节和知识点范围。将故事安排到段落中:引言中设置钩子,主歌中讲起源,铺垫中转折,副歌中第一个高潮,间奏中讲代价,铺垫2中制造悬念,副歌2中定义时刻,尾奏中回响。不同主题有不同的节奏:narrative-person.md、narrative-landmark.md、narrative-animal.md。

字幕:CJK 每行 ≤ 16 个字符,其他文字每行 ≤ 9 个词 / 42 个字符,最多 2 行,不得在数字或日期内部断开,从右到左的文字按从右到左布局。一个字幕必须在其镜头内可读(CJK 每秒 7 个字符,其他文字每秒 3.3 个词)。字形检查如果发现缺失字形(glyph_missing)则会失败,而不是渲染出豆腐块。

场景(#1 质量风险)

为每个场景名称编写 scenes/<name>.js:scene('<name>', function (g, s) { … }),为时间 s.t 绘制完整帧,使用 Canvas 2D 和 s.kit(纸张、笔触、墨染、水墨晕染、山、水、船、亭、卷轴、竹、月、砚、笔、印、梅、雁、袍子剪影、文字揭示、地图路线、时间线、计数、2.5D layer() 视差、构图 frame())。从 assets/style/exemplars/ 开始。规则(通过静态扫描和运行时强制执行):在 s.t 中具有确定性(s.kit.rng(seed),绝不使用 Math.random/Date/timers),无网络请求,无 DOM 或存储。在每个关键动作的提示点上计时(s.cue('seal', 0)),确保音效落在动作发生的帧上。绝不绘制逼真的面部。可以/不可以列表:style-pack.md。

导演循环(逐段进行)

node <skill>/scripts/chronicle.mjs scenes --project <p>             # 扫描 + 项目
node <skill>/scripts/chronicle.mjs render --project <p> --segment s1 # 开头 ~11 秒优先
# 查看 out/segments/s1/sheet.jpg 和 keyframes/*.jpg,对照可以/不可以列表,修复,重复
node <skill>/scripts/chronicle.mjs render --project <p> --segment s2 # 然后 s3, s4, s5
node <skill>/scripts/chronicle.mjs all --project <p>                 # 最终的确定性完整渲染 + 检查 + 报告

片段:s1 前奏 · s2 主歌 · s3 铺垫 + 副歌 · s4 间奏 + 铺垫2 · s5 副歌2 + 尾奏(或 --segment <start>-<end>)。每个片段最多 3 轮审查;每轮记录在 work/review.json 中:

{ "schema": "chronicle-review/1", "segments": [{ "id": "s1", "rounds": [{ "sheet": "out/segments/s1/sheet.jpg",
  "findings": ["标题与月亮重叠"], "fixes": ["月亮向右移动"], "open_blocking": [] }] }] }

如果某个片段没有审查轮次或仍存在阻碍性问题,check 会报告 visual_review 失败。对于无人值守的运行,all 直接渲染整部影片;审查要求仍会显示在报告中。

输出

out/film.mp4、out/captions.vtt、out/sheet.jpg、out/joins.jpg、out/report.json、out/report.en.html、out/report.zh-CN.html(双语,无论影片使用何种语言)。check 测量 LUFS 和真实峰值,在强拍上检查切换和段落,并将每个音效的起始点控制在动作的帧范围内;它无法判断混音听起来是否好——需要说明仍需要人来听。

限制

  • 无真人肖像;地图绝不显示国家或省界;GB 45438 的 AIGC 字段集在对照标准文本检查之前保持 verified: false(compliance.md)。
  • 三种风格包,每个包一个主题(或可选择音轨)。年份卡片:省略 count(从 0 向上计数的年份看起来像错误的日期)。
  • 城市地图不显示国家或省界;OpenStreetMap 图层裁剪到城市范围。
  • 事实词汇(数字、日期、保留表达)仅支持英语和中文;其他字幕语言使用英语规则。
  • 参考:pipeline.md、music.md、sources-allowlist.md、third-party-notices.md。

相关技能