Zennの記事執筆の速度と質の向上を図ってみる
挨拶
こんにちは、T4D4です。
実は今年の目標として、1月1記事書くことを目標にしているのですが、あまり筆が捗らないので頭を抱えています。理由は就活で忙しいとかそもそものネタが思いつかないとか色々とあるのですが、その中の1つとして執筆環境が良くないのではないか?というのがあります。そこで、今回は執筆環境(のレポジトリ)を整備することで記事執筆の速度と質の向上に挑戦しようと思います。
Markdownのフォーマッターを導入する
まず、最初に思いついたのは、markdownのフォーマッターの導入です。
自分は恥ずかしながらマークダウンをかなり適当に書いているので、フォーマッターを導入することで自分の記事の読書体験を向上できると思いました。
今回は以前TwitterのTLに流れてきた、ラムダノートのマークダウンのフォーマットを参考にしてみようと思います。
手動でやるのは面倒なので自動でフォーマットする環境を整備してみました。
markdownlintのルール作成
自動でマークダウンをフォーマットする手段としてmarkdownlintを導入してみました。
markdownlintはマークダウンのフォーマットをチェックするツールで、ルールをカスタマイズできます。
以下のようなルールを作成して、記事のフォーマットを統一しました。
以下はClaudeに作成させてみたカスタムルールです。
実際のClaudeとのやり取り
これで、markdownlint --rules ./lambda-note-rules.js <file_path>
を実行することで、指定したマークダウンファイルのフォーマットをチェックできます。
問題
- markdownlintの実行結果が読みにくい
身も蓋もないですが都度パス名を全部含んでいるので長くて読みにくいです。😔 - 厳密なルールの適用は難しい
場合によっては、ルールに厳密に従うと逆に読みにくくなったり不自然になってしまうことがあります。
以前からZennの記事を書くレポジトリにTextlintやテキスト校正くんは導入していました。
その恩恵は受けてきましたがたまにルールに従おうとすると逆に不自然な文章になってしまう為あえて無視するような場面がありました。
今回のマークダウンのフォーマットも同様の結果になると思われます。
AIで良い感じにする
上の2つの問題を解決する為に、近年目覚ましい進化を遂げているAIを活用できないかと考えました。
具体的には、AIを使って以下のような流れの処理を自動で行うことを目指しました。
- markdownlintを実行してフォーマットした結果を取得。
- フォーマット結果をAIに渡して、適切なメッセージに変換。
- ZennのMarkdown記法や文脈に合わせて不要な部分を除外する
- 除外後に修正するべき点が残っていなければマージする
GitHub Actionsを使って自動化する
GitHub Actionsを使って、上記の処理を自動化しました。
以下は、GitHub Actionsの設定ファイルの一部です。
`review-with-ai.yaml`の内容
name: 'Review with AI'
on:
pull_request:
branches: [ main ]
paths:
# 記事を保存しているディレクトリが変更されたときのみワークフローを実行する
- articles/*.md
jobs:
review:
permissions:
models: read
# ファイルをプロンプトとして渡すために必要な権限
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
# いつものチェックアウト
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
# 変更されたファイルを取得する
- name: Get changed files in the articles folder
id: changed-files-specific
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5
with:
files: |
articles/**
# markdownlintを実行して結果をファイル出力
- name: Setup markdownlint-cli
if: steps.changed-files-specific.outputs.all_changed_files != ''
run: npm install -g markdownlint-cli
- name: Run markdownlint
if: steps.changed-files-specific.outputs.all_changed_files != ''
run: |
markdownlint --rules ./lambda-note-rules.js ${{ steps.changed-files-specific.outputs.all_changed_files }} 2> markdownlint-result.txt || true
# AI-inferenceアクションでエラーをふるいにかけて可読化
- name: Run AI Inference for markdownlint output
id: ai-inference
if: steps.changed-files-specific.outputs.all_changed_files != ''
uses: actions/ai-inference@d645f067d89ee1d5d736a5990e327e504d1c5a4a # v1.1.0
with:
system-prompt: |
あなたはプロフェッショナルなテックブログ・レビューアー AIです。
あなたの役割は、開発者や技術者が書いたブログ記事を技術的正確性・表現の明確さ・構成の論理性・読者への価値提供という観点でレビューし、フィードバックを提供することです。
入力として渡されるのは markdownlint の出力結果です。Zenn の Markdown 記法や記事の文脈から修正不要なエラーや、コードブロック内のサンプルコードに書かれたコメントは除外し、修正が必要なものだけを分かりやすくまとめてください。
出力は日本語でお願いします。
prompt-file: "markdownlint-result.txt"
# CLIでPRにコメント投稿
- name: Comment on PR
id: comment
if: steps.changed-files-specific.outputs.all_changed_files != ''
env:
RESPONSE: ${{ steps.ai-inference.outputs.response }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "${RESPONSE}" > comment.txt
gh pr comment ${{ github.event.pull_request.number }} --body-file comment.txt
echo "Commented on PR #${{ github.event.pull_request.number }} with response: $RESPONSE"
# マージ可否の判定
- name: Fail if errors remain
if: steps.changed-files-specific.outputs.all_changed_files != ''
run: |
# AI-inferenceの出力内に「修正が必要なエラーはありません」等の文言があればOK、それ以外ならfail
if grep -q -E '修正が必要なエラーはありません|マージ可能|No errors requiring fixes' comment.txt; then
echo "No actionable errors. PR can be merged."
else
echo "There are remaining errors. PR cannot be merged."
exit 1
fi
ついでに(?)メインブランチを保護する為のルールセットも設定しました。
本題ではないですし、よくありそうな設定だと思うので簡単に説明しますと、
- 保護する対象のブランチにmainを指定
- 削除を防止
- マージ前にPRを必須とする
- PRに新しいコミットが追加された時に古い承認を無効にする
- 指定したステータスチェックの必須化
- 古い状態のブランチがmainにマージされるのを防ぐ
- 新しいブランチの作成はステータスチェックをパスしなくても許可する
- review(今回作成するAction)という名前のGitHub Actionsの結果を参照するように指定
といったような具合の設定をしました。
とりあえず動かしてみます。
無事に動きました。でもまだ少しメッセージがわかりにくいですね。
今度は、1つ目は良い感じですが2つ目は途中で途切れてしまっていますね。
ここまでの感想
最初に考えていたアイディアを7割ぐらいのクオリティで実現できてのではないでしょうか?
しかし正直な感想としては、すべてのエラーに対処すると逆に違和感が生まれてしまうのである程度は無視する必要が出て来るのですが自分の判断がAIの判断と揃わない場合が多々あったり、出たエラーの内容を確認→修正→コミット&プッシュ→再度アクションの実行→修正結果の確認という微妙に手間のかかる手順を繰り返す必要があったりと体験が良くないです。
あと、当然ですがアクションを実行する度にクレジットを使うので(この程度なら大した額にはならないと思いますが)お財布に優しくないです。
なので、Textlintと同様にMarkdownlintもローカルで動かしてコミット前に動かすぐらいが良さそうな気がします。
AIに自動レビューだけしてもらう
GitHub Actionsを使って自動でmarkdownlintを実行し、メッセージをAIに簡単にしてもらうのは上手く行きませんでした。
しかし、GitHubのレポジトリにルールセットを導入したのとローカルでMarkdownlintを使用するようにしたのだけでは効果が薄い気がします。(あと、AIを使いたい)
なので?今度はAIに複雑で長い命令はせず、単純にレビューだけをして貰おうと思います。
マージ前に自動でレビューさせ、レビューの結果を見て必要があれば修正、問題無さそうならマージぐらいの感覚での運用を目指してみます。
Gemini Code Assistを導入する
AIで調べてみたところ、Gemini Code Assistをレポジトリに追加するとPRを出したら勝手にレビューしてくれるみたいです。PricingもFreeとのことでお財布に優しそうです👀
詳細はこちら
動作確認
早速動作しました。
最初に変更についてのsummary、その下には、上の方のみを抜粋したので切れていますがHilightが列挙されていて、どのような変更がされたのかがわかります。
さらに、その下にはCode Reviewが続いており、いくつか修正した方が良い点について教えてくれていました。
英語なので読むのに気合が必要な場合もありますが、結構満足のいく結果になりました。
まとめ
今回は、記事執筆の質と速度の向上を図る為にレポジトリを整備してみました。
中途半端な部分はありますし、導入したばかりなので今後継続的に改良していければと思います。
とくにGeminiに関しては、今回はとりあえず不要そうなので見送りましたが公式のドキュメントによると、.geminiディレクトリを用意すると色々とカスタマイズができそうなので今後不満があれば挑戦してみようかと思います。
Discussion