Color Prismのコンテンツ制作は、適宜人間の判断を挟むために、タスクごとに複数のスキルに分けて進めている。
CGページを例にすると、1つの記事を公開するまでのタスクの流れは、大まかに次のような感じ。

  1. ページの雛形ファイルの作成(/create-cg-pageスキル)
  2. 記事に含めたいトピック・キーワードの洗い出し(人間)
  3. 関連リンク先ページの作成(/prepare-link-targetsスキル)
  4. 草稿の執筆(/author-style-writerスキル)
  5. 構成やデモの配置を確定(人間)
  6. 図版やデモの実装と埋め込み(/add-threejs-demoスキル)
  7. 文章の調整・編集指示の追加(人間)
  8. 編集指示に基づく一括修正(/apply-edit-requestsスキル)
  9. 納得のいく仕上がりになるまで調整(人間)
  10. 公開時タスクの実行(/publish-articleスキル)

どのスキルも、自分の担当が終わったら、次に叩くべきスキルとその引数を案内して止まるようになっている。

準備:雛形ページの作成とページ一覧の更新

ページ用の空ルートファイルの作成ですら、スキルを経由して行う。それには、記事一覧データとの整合性を担保する目的もある。

未作成ページも含めたページ一覧のデータはYAMLファイルで管理していて、記事一覧ページはこのYAMLデータをもとに生成されるようになっている。

title: 変換と投影
summary: 座標変換・投影・画像の幾何学的変換
sections:
  - heading: 図形の幾何学的変換
    id: coordinate-transform
    links:
      - slug: coordinate-systems
      - slug: basic-transformations
      - slug: transformation-composition
      - slug: reflection-and-skew
      - slug: affine-transformation
      - slug: projective-transformation
  - heading: ビューイングパイプライン
    id: viewing-pipeline
    links:
      - slug: viewing-pipeline-transformations
      - title: 階層モデリング # CG 2-4-4
        group: ["CG"]

7行目のようなslugが決まっているものは作成済みページで、17~18行目のようにtitleしか決めていないものはルートファイルすら存在しない未作成ページ。

ルートファイルを作成するときは、/create-cg-pageスキルを使う。このスキルはSvelteKitのルーティング規約に沿ったページ用の雛形ファイルの作成、YAMLファイルの書き換えを行い、routesディレクトリの構造とYAMLデータの整合性を保つ。タイトルからslugを考えるのもスキルの仕事。

/create-cg-page 階層モデリング
作成される雛形ファイル (+page.svx)
---
layout: guide-content
title: 階層モデリング # CG 2-4-4
group: ["CG"]
draft: true
---
 
## TODO

草稿:キーワードを起点とした自動執筆

CG記事のコンテンツは、記事に含めたいキーワードの羅列から自動生成し、それをベースとしながら、デモ追加などの肉付け・細かな調整を行って納得のいく形になってから公開する。

たとえば、「写実的表現とリアリティの要素」というタイトルの記事なら、こんな感じでキーワードを洗い出しておく。

三面図, 工業製図, 線画, 物体の色, 照明効果, 透視図, ピンホールカメラ, 陰影, 建築, 照明設計, 材質, 反射特性, 映り込み, ガラス, 透過, 屈折, モデリング, フォトリアリスティックレンダリング, 稜線, 透視投影, ワイヤフレーム表示, デプスキューイング, 輝度, 陰線消去, 隠面消去, シェーディング, 陰付け, テクスチャ, レンダリング, リアリティ, 遠近感, 可視面表示, 表面の明るさ, 影, デプスキュー, 表面の滑らかさ, 反射率, 色, 光の物理的性質, 反射光, 間接光, 大気, 散乱, 減衰, 霧, 霞, 空, 雲, プリズム, 分散, シャボン玉, コンパクトディスク, 干渉, 回折, ポリゴン, 曲面, 濃淡, スムーズシェーディング, バンプマッピング, テクスチャマッピング

まずは関連リンク先を用意

わからない関連用語を気軽に学びに行けるのは、ハイパーリンクを備えるWebならではの体験だと思っている。Webならリンクをワンクリック、しかし本では巻末にある「参考文献」リストを参照し、図書館に行って探さなければならないのだ。

なので、本文に出てくる語のうち、他ページで詳しく取り上げているものは積極的にリンクにしたい。
ただ、リンク先の関連ページがまだできていない場合に「そっちのページができてからリンクを設定しよう」なんて後回しにしていると、絶対に忘れる。リンクし忘れた語は本文に埋もれて、後から拾い直すのは大変なのだ。

そのため、関連ページ側の本文がまだ書けていなかったとしても、近日中に書くつもり…と心の中で弁解して、リンクは設定してしまう方式にしている。

そこで執筆の前に実行するのが、/prepare-link-targetsというスキル。

/prepare-link-targets photorealism-and-reality-elements 三面図,工業製図,線画,...

このスキルは、引数で与えられたキーワードを主題として扱う記事が他に存在しないかを判断する。
主題として取り上げる記事が他に存在すれば、そのページをリンク先として設定するために、必要に応じて/create-cg-pageスキルでページを作成する。

草稿の自動執筆

関連リンク先が出揃ったら、/author-style-writerスキルで草稿の執筆を行う。

/author-style-writer photorealism-and-reality-elements 三面図,工業製図,線画,...

第一引数には書きたい記事のslug、第二引数には記事に含めたいキーワードを渡す。スキルは、キーワード群をもとにこの記事の主題を把握し、解説の流れやセクションの構成を設計し、すべてのキーワードを適切な重要度で含めながら、記事本文を執筆する。

なぜアウトライン設計からスキルに丸投げできるのか?という詳細については、次の記事で仕組みがわかるかもしれない。

図版やデモの実装

/author-style-writerスキルが作成した草稿には、あると良さそうな図版やデモの提案がすでにある程度書かれている。

この指示を参考にアイデアを練り、図版やデモを作成してページに埋め込む作業は専用のスキルに依頼する。
SVG図版のスタイルガイドと、Three.jsデモの実装規約を分けて管理するため、SVG図版は/svg-diagram-componentスキル、Three.jsデモは/add-threejs-demoスキル、というように分業している。

編集:編集指示の見える化と一括修正スキル

生成された草稿を人手で微調整していくが、AIに修正を依頼したい箇所も出てくる。

  • 複数箇所に同じ書式修正を適用したい場合
  • 私自身に理解不足があり、AIに加筆してもらった上で精査したい場合
  • 曖昧な表現の意図を確認したい場合
  • 文章ではなく、デモに対する修正
  • etc.

このような修正点を一時的に記事に書き入れるにあたって、これまでは、万が一消し忘れたまま公開してしまうことを避けるため、ページには表示されないコメントとして書き入れていたかもしれない。

しかし今は、人間が最終確認を行っていた頃とは違い、要修正箇所やメモ書きの残存は、AIを使った仕組みを組み込むことで自動的に・確実にチェックすることができる。

そこで、執筆中の段階では、修正点メモも堂々とページに表示されるようにしている。エディタでしか表示されない修正メモコメントと、ブラウザに表示される本文を行ったり来たりする必要もなく、人間としても残タスクがわかりやすくて快適。

役割を分けた編集指示ディレクティブ

修正点メモは、のちにそのままAIへの編集指示とすることができる。
そのため、修正内容の種別はAIがわかるようにしておきたく、mdsvexの独自ディレクティブを複数種類用意して、編集指示を種別に合ったディレクティブで囲む方式にしている。

背景付きのブロックは編集指示
面が隙間なく閉じた不透明な物体では、裏を向いた面は必ず手前側の面に隠れます。
視点からは見えない、物体の裏面はそもそも表示されないため、わざわざレンダリングする必要はありません。このような不可視となる裏面(back face)をあらかじめ除外する処理が:Anki[バックフェースカリング]です。
 
:::Pending
[隠面消去](/cg/rendering/photorealism-and-reality-elements/)では、重なり合う面のどちらが手前にあるかを確かめて、隠れる面を落としていきます。稜線を対象にする陰線消去も同じで、扱う面や線が増えるほど、この判定は重くなります。
:::
 
## :WithGroupTag[裏面の判別]{group="{['CG']}"}
 
:::Fix
インライン数式の前後には空白を空ける
:::
 
面が裏を向いているかどうかは、面に:Anki[垂直]な向きと、その面から:Anki[視点]を見た向きを比べればわかります。
 
面に垂直で表側を向くベクトルが、その面の:Anki[法線]です。この:Anki[法線ベクトル]を$$\vec{n}$$、面から視点へ向かう:Anki[視点方向ベクトル]を$$\vec{e}$$とします。
 
:::Add
:Anki[法線]とは何かについてNote形式で補足
:::
 
面が視点側を向いているなら、$$\vec{n}$$と$$\vec{e}$$は同じ側を指すため、なす角は:Anki[鋭角](直角より小さい角)になります。逆に、裏を向いた面では、なす角が:Anki[鈍角](直角より大きい角)になります。
  • :::Todo:図版・デモが欲しい箇所。中身にはどんな図かを書く
  • :::Add:加筆したい箇所。中身は加筆内容の指示
  • :::Delete:消す予定の記述。中身は削除対象の本文そのもの
  • :::Fix:すでにある図版・デモ・文章への修正指示
  • :::Pending:残すか消すか決めきれていない文章

書き溜めた指示をまとめて反映

こうして書き溜めた編集指示は、/apply-edit-requestsスキルを一回呼び出せばまとめて対応してくれる。

/apply-edit-requests photorealism-and-reality-elements

/apply-edit-requestsスキルは、ディレクティブの種類によって適切なスキルを使い分けて、順に対応を行う。

:::Add:::Deleteは文章に対する指示なので、/author-style-writerスキルが呼び出される。

:::Fixは実はtargetPropsを指定することができ、省略時はtextで文章に対する修正指示だが、SVG図版への修正指示ならsvg、Three.jsデモへの修正指示ならdemoを指定する。

:::Fix
インライン数式の前後には空白を空ける
:::
 
:::Fix{target="svg"}
グラフの縦軸に単位を入れる
:::
 
:::Fix{target="demo"}
モバイルだとベクトルが太すぎ、ラベルが小さすぎて読めないので、調整
:::

このPropsの値が、そのまま担当スキルの振り分けになる。svgなら/svg-diagram-componentスキル、demoなら/add-threejs-demotextなら/author-style-writerが修正処理を引き取る。

なお、:::Todo:::Pending/apply-edit-requestsスキルでは変更されない。
:::Todoは図版・デモの新規作成であり、セッションを分けて対話しながらじっくり検討・実装した方がよい。
:::Pendingで囲まれた文章を採用するか削除するかどうかは人間が決めたいので、スキルでは読み飛ばし、勝手に削除しないようにしている。

公開:公開時ガードとOGP画像の生成

フロントマターからdraft: trueを外すと、記事が公開状態になる。公開前に必要な作業一式を担うのが/publish-articleスキル。

/publish-article photorealism-and-reality-elements

このスキルは、まずは編集指示ディレクティブが本文に残っていないかを確認する。1件でも残っていたら、公開時タスクへは進まず、報告して止まる。報告時には、具体的にどのような対応をすべきか、コピペ可能な形でコマンドを示してくれる。

/svg-diagram-component 水晶体分光透過率のグラフ
/add-threejs-demo hidden-surface-removal-methods Zバッファ法の深度比較
/apply-edit-requests hidden-surface-removal-methods

この残存チェックが通ったら、スキルは次のタスクに進む。

  1. 図版・デモがあるページの場合、フロントマターにvisual: trueを追加
  2. OGP画像の生成(/generate-ogp-imageに処理を委譲)
  3. 各種タスクリストの更新(後述)

フロントマターにvisual: trueを追加することで、記事一覧ページ上で「図解」タグが表示されるようになる。なにせ記事数が多いので、まずは図版・デモのあるページを体験してもらい、サイトの魅力を知ってほしい…

ちなみに、Gitコミットコマンドをラップして、さまざまなコミット前処理を行った上で適切な粒度に分割してコミットしてくれる/commit-thisというスキルも用意している。
draft: trueを削除した状態でこのスキルにコミットを依頼すると、コミット前に/publish-articleスキルが自動的に呼ばれるようになっている。万が一人間が/publish-articleを呼び忘れても、どちらにせよコミット前に公開時タスクの実行が担保される仕組み。

循環:仕組みを保ち、成長させるために

これで記事は完成だが、執筆状況などを管理し、今後書く記事をよりよい品質にするために、公開後にもいくつか工夫がある。

分析対象として認識させるコミット規約

/commit-thisスキルが記事の.svxファイルをコミットするときは、コミットメッセージを次のように書き分けるように指示している。

  • AIに書かせた初稿:<title> [ai-draft]
  • 初稿ではないが、AIによる修正しか含まない:<title>:修正内容 [ai-draft]
  • 人手で調整した:<title>:調整内容[ai-draft]は付けない)

この[ai-draft]は、公開済み記事を分析してスタイルガイドの更新を行う/author-style-analyzerスキルのために付与している。/author-style-analyzerスキルは、AI草稿のコミットと、その次に行った人手修正のコミットの差分を比較して、私が潰したい「AIっぽさ」を潰すためのルールを分析する。その結果は推敲ガイドrefine-style.mdにまとめられ、次回以降の/author-style-writerによる執筆時に使われるようになる。

その細かい仕組みについては、やはり次の記事を読んでほしい…

例外が1つだけあり、/author-style-writerスキルの編集モードで私が指示した修正は、AIによる書き換えでも[ai-draft]を付けないようにしている。指示の内容自体は私の判断なので、この場合は「依頼された修正」扱いにして、コミットの本文にはどんな依頼だったか・なぜそう変えたかを記す。後からコミット履歴を調べて人間の判断を分析する際に、その理由を辿れるようにするため。

このように、執筆に関係するコミットは分析器への入力でもある。規約を守らずにコミットすると、その記事は差分分析の対象にはならないため、規約を遵守したコミットメッセージの生成も/commit-thisスキルで行われるようにしている。

タスクリストの整合性の担保

最後に、次の2つのタスクリストの更新を行う。

  • ogimage/OGP-TASKLIST.md:OGP画像生成済みのページの記録
  • writing-guides/STYLE-ANALYSIS-TASKLIST.md:analyzerによって分析済みのページの記録

これらのタスクリストの最新状態との同期処理は、差分を検出するスクリプトに委譲される。

一覧YAMLを編集したあと
node scripts/sync-tasklists.mjs --check   # 差分を報告するだけ(差分があれば exit 1)
node scripts/sync-tasklists.mjs --write   # 差分を書き込む

節をまたいだ項目の移動や、片方のリストにだけある欠落を拾わなければならず、スキルに任せるとかえって取りこぼす。数える・書き写すという知能が特に必要ない単純作業は、AIよりもスクリプトの方が確実だと考えている。

ちなみに、これらのタスクリストが最新の状態になっているかどうかも、/commit-thisスキルで最終チェックされる。
記事の公開だけでなく、ページのタイトルを変更したり、YAML内で記事を並べ替えただけでも、これらのタスクリストとの整合をとる必要がある。そのため、/commit-thisスキルは必要に応じてタスクリスト更新スクリプトを実行してからコミットを行う。

このタスクリストがあることで、たとえば/author-style-analyzerスキルを呼び出すときも、「未分析の記事」のような一括指定が可能になる。

/author-style-analyzer 未分析の記事