ADR 0001: grok 自動投稿は DeepSeek で精査・リライトしてから公開する

背景

トップページの「情報」タブ (_layouts/home.htmlgrok-generated タグ絞り込み) に並ぶ記事は、 scripts/grok-post.mjs が Grok の応答を内容の精査なしにそのまま _posts/ へ書き出したものだった。

既存の検査はすべて体裁のみ:

書かれている内容が正しいかは一切見ておらず、末尾の「事実確認を行っていない」ラベルで免責していた。 これは親 #84 Decision Log #1「AI で裏付けをしない」(トークン節約と自立性を優先) に基づく 意図的な設計だったが、公開面の品質を優先して方針を転換する (issue #108)。

決定

投稿前に DeepSeek API で記事を精査し、本文自体をリライトしてから _posts/ に書き出す。

工程

Grok 応答 → sanitize → title/body 抽出
  → link-check.mjs      : 本文全体の URL を HEAD/GET で生存確認
  → deepseek-review.mjs : 死リンク情報を添えて DeepSeek に精査・リライトさせる
  → findIntroducedUrls  : 元記事に無い URL が増えていたらリライトを棄却
  → link-check.mjs      : 公開される本文を再検査。死リンクが残っていれば棄却
  → renderPost(reviewed): ラベルと tags を精査の成否で切り替える

失敗時は fail-open、ただしラベルは偽らない

DeepSeek 呼び出しが失敗したとき (キー不在 / HTTP エラー / タイムアウト / 空 content / リライト結果が文字数レンジ外) は、Grok 原文+従来の「事実確認を行っていない」ラベルで投稿する。

リンク生存判定は「誤って殺さない」側に倒す

404 / 410 だけを死と判定する。403 (bot 遮断)・5xx (一時障害)・タイムアウト・DNS 失敗は 「判定不能」として死リンク扱いしない。生きている URL を誤って削除させる方が害が大きいため。

さらに、死判定は必ず GET で確認してから確定するHEAD を未実装のまま 404 を返し GET なら 200 を返すサーバーが実在し、HEAD だけで確定すると生きた出典を記事から 消してしまう。GET が届かなかった場合も「判定不能」として死リンク扱いしない。

捨てた案

変えてよい前提 / 壊すと危ない前提